feat(stack): add Paperless-ngx document management system
- Add paperless.yml stack (port 8091, documents.schweitz.net) - Uses shared postgres (DB: paperless) and redis (DB 8) - ClamAV installed on host for virus scanning (port 3310) - Add Paperless integration to library-desk stack - Update CONTAINERS.md with Paperless and ClamAV profiles - Add Portainer and NPM API documentation to setup-new-host.md - Update redis-shared.yml and postgres-shared.yml with Paperless refs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+67
-5
@@ -1,7 +1,7 @@
|
||||
# Container Reference - tower-of-joy Infrastructure
|
||||
|
||||
> **Last Updated:** 2025-12-14
|
||||
> **Total Services:** 24 containers across 19 stacks
|
||||
> **Last Updated:** 2025-12-24
|
||||
> **Total Services:** 26 containers across 20 stacks
|
||||
> **System:** Intel i7-6700, RTX 2080 Ti (11GB VRAM), 16GB RAM, Zorin OS 16.3
|
||||
|
||||
---
|
||||
@@ -34,6 +34,8 @@
|
||||
| **Library Desk** | 8089 | http://192.168.86.149:8089 | LAN | No | 4 | ✅ Running (external) |
|
||||
| **AMP (Game Server)** | Varies | http://192.168.86.149:8081 | LAN | No | - | ✅ Running |
|
||||
| **Home Assistant** | 8123 | https://housekeeping.schweitz.net | Internet | No | - | ✅ Running |
|
||||
| **Paperless-ngx** | 8091 | https://documents.schweitz.net | Internet | No | 8 | ✅ Running |
|
||||
| **ClamAV** | 3310 | N/A (host service) | No | No | - | ✅ Running |
|
||||
|
||||
### External Domains (SSL via Let's Encrypt)
|
||||
- **home.schweitz.net** → Organizr (Protected by Authentik SSO)
|
||||
@@ -45,6 +47,7 @@
|
||||
- **code.schweitz.net** → Code-Server (host service)
|
||||
- **amp.schweitz.net** → AMP Game Server
|
||||
- **housekeeping.schweitz.net** → Home Assistant
|
||||
- **documents.schweitz.net** → Paperless-ngx
|
||||
- **tatlock.schweitz.net** → (Reserved)
|
||||
|
||||
### External Repositories
|
||||
@@ -122,8 +125,8 @@ PostgreSQL Shared is a centralized PostgreSQL 17 database server providing isola
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | docker-dataplane network |
|
||||
| **Databases** | `authentik` (Authentik SSO), `gitea` (Git hosting), `organizr` (Organizr dashboard), `postgres` (default/admin) |
|
||||
| **Database Users** | `authentik_user`, `gitea_user`, `organizr_user`, `postgres` (superuser) |
|
||||
| **Databases** | `authentik` (Authentik SSO), `gitea` (Git hosting), `organizr` (Organizr dashboard), `paperless` (Document management), `postgres` (default/admin) |
|
||||
| **Database Users** | `authentik_user`, `gitea_user`, `organizr_user`, `paperless_user`, `postgres` (superuser) |
|
||||
| **Health Check** | `pg_isready -U postgres` (30s interval) |
|
||||
| **Backup Strategy** | `/backups` volume for pg_dump exports |
|
||||
|
||||
@@ -158,7 +161,7 @@ Redis Shared is a centralized Redis 7 key-value store providing cache, session s
|
||||
| **Resource Limits** | None |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | docker-dataplane network |
|
||||
| **Database Allocation** | DB 0: Authentik, DB 1: Tatlock (memory), DB 2: Wiki.js, DB 3: Scheduler, DB 4: Library Desk, DB 5: SearXNG, DB 6: Tatlock (benchmarks), DB 7: Nextcloud, DB 8-15: Available |
|
||||
| **Database Allocation** | DB 0: Authentik, DB 1: Tatlock (memory), DB 2: Wiki.js, DB 3: Scheduler, DB 4: Library Desk, DB 5: SearXNG, DB 6: Tatlock (benchmarks), DB 7: Nextcloud, DB 8: Paperless, DB 9-15: Available |
|
||||
| **Persistence** | AOF (Append-Only File) enabled for durability |
|
||||
| **Health Check** | `redis-cli ping` returns PONG (30s interval) |
|
||||
| **Connection String** | `redis://redis-shared:6379/0` (DB 0), `redis://redis-shared:6379/1` (DB 1), etc. |
|
||||
@@ -471,6 +474,65 @@ Home Assistant is an open-source smart home automation platform that integrates
|
||||
|
||||
---
|
||||
|
||||
### Paperless-ngx
|
||||
|
||||
Paperless-ngx is a document management system that transforms physical documents into a searchable online archive with automatic OCR text recognition, tagging, and full-text search. It provides a web interface for uploading, organizing, and retrieving documents with support for correspondents, document types, and custom fields. The service integrates with Library Desk for document indexing and uses ClamAV for virus scanning of uploaded files. Configs and database are stored on SSD (backed up), while documents are stored on HDD for capacity. Uses shared PostgreSQL for metadata and shared Redis (DB 8) for task queuing.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Image** | `ghcr.io/paperless-ngx/paperless-ngx:latest` |
|
||||
| **Container Name** | `paperless` |
|
||||
| **Access URL (LAN)** | http://192.168.86.149:8091 |
|
||||
| **Access URL (Public)** | https://documents.schweitz.net |
|
||||
| **External Access** | Yes (via NPM reverse proxy with SSL) |
|
||||
| **Port Mapping** | 8091:8000 (HTTP) |
|
||||
| **Network Mode** | Bridge (docker-dataplane) |
|
||||
| **Restart Policy** | `unless-stopped` |
|
||||
| **Volume Mounts** | SSD (backed up): `~/docker-data/paperless/data:/usr/src/paperless/data`; HDD: `/mnt/media/paperless/media:/usr/src/paperless/media`, `/mnt/media/paperless/consume:/usr/src/paperless/consume`, `/mnt/media/paperless/export:/usr/src/paperless/export` |
|
||||
| **Environment** | `PAPERLESS_DBHOST=postgres-shared`, `PAPERLESS_REDIS=redis://redis-shared:6379/8`, `PAPERLESS_URL=https://documents.schweitz.net`, `TZ=Europe/Amsterdam` |
|
||||
| **Resource Limits** | Memory: 4GB limit, 512MB reservation |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | PostgreSQL Shared, Redis Shared (DB 8), NPM (reverse proxy), ClamAV (host) |
|
||||
| **Database** | PostgreSQL `paperless` on postgres-shared |
|
||||
| **Health Check** | `curl -f http://localhost:8000` (30s interval) |
|
||||
| **Features** | OCR (eng+nld), document tagging, full-text search, correspondents, custom fields, webhooks |
|
||||
| **Integration** | Library Desk (webhook on document added), ClamAV (virus scanning) |
|
||||
|
||||
---
|
||||
|
||||
### ClamAV
|
||||
|
||||
ClamAV is an open-source antivirus engine running on the host OS, providing virus scanning capabilities for the entire server and accessible via TCP socket for container-based services like Paperless-ngx. It includes automatic virus definition updates via freshclam and can perform both on-demand and real-time scanning. Running on the host provides better security isolation than containerized scanning and allows scanning of the host filesystem directly.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Deployment Type** | Host-based systemd service (NOT containerized) |
|
||||
| **Binary Location** | `/usr/bin/clamd`, `/usr/bin/clamdscan` |
|
||||
| **Service Names** | `clamav-daemon.service`, `clamav-freshclam.service` |
|
||||
| **Access URL** | N/A (TCP socket only) |
|
||||
| **External Access** | No (internal service) |
|
||||
| **Port Binding** | 0.0.0.0:3310 (TCP socket for remote scanning) |
|
||||
| **Restart Policy** | `always` (systemd) |
|
||||
| **Config Location** | `/etc/clamav/clamd.conf`, `/etc/clamav/freshclam.conf` |
|
||||
| **Database Location** | `/var/lib/clamav/` (virus definitions) |
|
||||
| **Resource Usage** | ~2-4GB RAM (virus definitions loaded in memory) |
|
||||
| **GPU Required** | No |
|
||||
| **Dependencies** | None (host service) |
|
||||
| **Update Schedule** | Automatic via freshclam (checks multiple times daily) |
|
||||
| **Connection String** | `clamav://192.168.86.149:3310` |
|
||||
| **Features** | On-demand scanning, TCP socket API, automatic updates, host filesystem access |
|
||||
|
||||
**Usage from containers:**
|
||||
```bash
|
||||
# Test connectivity
|
||||
nc -zv 192.168.86.149 3310
|
||||
|
||||
# Scan a file (from host)
|
||||
clamdscan /path/to/file
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Samba
|
||||
|
||||
Samba provides SMB/CIFS network file sharing for seamless access to homelab storage from Windows, macOS, Linux, and mobile devices, exposing curated shares for media libraries, downloads, and backups with configurable read-only and read-write permissions. It runs as a single container on the samba_default network, serving three shares: Media (read-write access to Jellyfin content), Downloads (read-write for torrent clients), and Backups (read-only for safe data recovery). The service uses password authentication for the user jpmschweitzer and stores its minimal configuration on the SSD while directly mounting HDD paths for zero-copy file access with native performance.
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
# Plan: Add Paperless-ngx and ClamAV
|
||||
|
||||
## Overview
|
||||
|
||||
Add document management (Paperless-ngx) and host-level virus scanning (ClamAV) to the tower-of-joy infrastructure.
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Choice |
|
||||
|----------|--------|
|
||||
| Database | Use `postgres-shared` (new DB: `paperless`) |
|
||||
| Redis | Use `redis-shared` DB 8 |
|
||||
| ClamAV | Host OS installation (full server protection) |
|
||||
| External Access | Yes, at `documents.schweitz.net` via NPM |
|
||||
| Network | `docker-dataplane` |
|
||||
| Stack | Separate `paperless.yml` stack |
|
||||
|
||||
## Services
|
||||
|
||||
| Service | Port | Location | Purpose |
|
||||
|---------|------|----------|---------|
|
||||
| Paperless-ngx | 8091 | Container | Document management, OCR |
|
||||
| ClamAV | 3310 | Host OS | Virus scanning + server protection |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: ClamAV Host Installation (requires sudo)
|
||||
|
||||
**User must run these commands manually:**
|
||||
|
||||
```bash
|
||||
# Install ClamAV
|
||||
sudo apt update
|
||||
sudo apt install clamav clamav-daemon -y
|
||||
|
||||
# Stop freshclam to update definitions
|
||||
sudo systemctl stop clamav-freshclam
|
||||
sudo freshclam
|
||||
sudo systemctl start clamav-freshclam
|
||||
|
||||
# Configure clamd to listen on network
|
||||
sudo sed -i 's/^#TCPSocket.*/TCPSocket 3310/' /etc/clamav/clamd.conf
|
||||
sudo sed -i 's/^#TCPAddr.*/TCPAddr 0.0.0.0/' /etc/clamav/clamd.conf
|
||||
|
||||
# If TCPSocket/TCPAddr not present, add them
|
||||
grep -q "^TCPSocket" /etc/clamav/clamd.conf || echo "TCPSocket 3310" | sudo tee -a /etc/clamav/clamd.conf
|
||||
grep -q "^TCPAddr" /etc/clamav/clamd.conf || echo "TCPAddr 0.0.0.0" | sudo tee -a /etc/clamav/clamd.conf
|
||||
|
||||
# Restart daemon
|
||||
sudo systemctl restart clamav-daemon
|
||||
|
||||
# Enable at boot
|
||||
sudo systemctl enable clamav-daemon clamav-freshclam
|
||||
|
||||
# Verify
|
||||
clamdscan --ping 3
|
||||
```
|
||||
|
||||
### Phase 2: Create Storage Directories
|
||||
|
||||
```bash
|
||||
# SSD - configs and working data
|
||||
mkdir -p ~/docker-data/paperless/{data,consume,export}
|
||||
|
||||
# HDD - document storage
|
||||
mkdir -p /mnt/media/paperless/media
|
||||
```
|
||||
|
||||
### Phase 3: Create PostgreSQL Database
|
||||
|
||||
```bash
|
||||
docker exec -i postgres-shared psql -U postgres <<'EOF'
|
||||
-- Paperless database
|
||||
CREATE DATABASE paperless;
|
||||
CREATE USER paperless_user WITH PASSWORD '<generate-secure-password>';
|
||||
GRANT ALL PRIVILEGES ON DATABASE paperless TO paperless_user;
|
||||
\c paperless
|
||||
GRANT ALL ON SCHEMA public TO paperless_user;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO paperless_user;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO paperless_user;
|
||||
EOF
|
||||
```
|
||||
|
||||
### Phase 4: Create Stack File
|
||||
|
||||
**Create:** `stacks/paperless.yml`
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
# Paperless-ngx - Document Management System
|
||||
# Port: 8091 (HTTP)
|
||||
# GPU: No
|
||||
# External: documents.schweitz.net
|
||||
|
||||
services:
|
||||
paperless:
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
container_name: paperless
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8091:8000"
|
||||
volumes:
|
||||
# SSD - config and working data
|
||||
- /home/jpmschweitzer/docker-data/paperless/data:/usr/src/paperless/data
|
||||
- /home/jpmschweitzer/docker-data/paperless/consume:/usr/src/paperless/consume
|
||||
- /home/jpmschweitzer/docker-data/paperless/export:/usr/src/paperless/export
|
||||
# HDD - document storage
|
||||
- /mnt/media/paperless/media:/usr/src/paperless/media
|
||||
environment:
|
||||
# Database (shared PostgreSQL)
|
||||
PAPERLESS_DBENGINE: postgresql
|
||||
PAPERLESS_DBHOST: postgres-shared
|
||||
PAPERLESS_DBPORT: 5432
|
||||
PAPERLESS_DBNAME: paperless
|
||||
PAPERLESS_DBUSER: paperless_user
|
||||
PAPERLESS_DBPASS: ${PAPERLESS_DB_PASSWORD}
|
||||
|
||||
# Redis (shared, DB 8)
|
||||
PAPERLESS_REDIS: redis://redis-shared:6379/8
|
||||
|
||||
# Security
|
||||
PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}
|
||||
|
||||
# URLs
|
||||
PAPERLESS_URL: https://documents.schweitz.net
|
||||
PAPERLESS_ALLOWED_HOSTS: "*"
|
||||
PAPERLESS_CORS_ALLOWED_HOSTS: "http://localhost:8091,https://documents.schweitz.net"
|
||||
|
||||
# OCR Settings
|
||||
PAPERLESS_OCR_LANGUAGE: eng+nld
|
||||
PAPERLESS_OCR_MODE: skip
|
||||
PAPERLESS_OCR_OUTPUT_TYPE: pdfa
|
||||
|
||||
# Webhooks (for Library Desk integration)
|
||||
PAPERLESS_WEBHOOKS_ALLOW_INTERNAL_REQUESTS: "true"
|
||||
|
||||
# Admin user (created on first run)
|
||||
PAPERLESS_ADMIN_USER: admin
|
||||
PAPERLESS_ADMIN_PASSWORD: ${PAPERLESS_ADMIN_PASSWORD}
|
||||
|
||||
# Timezone
|
||||
TZ: Europe/Amsterdam
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2G
|
||||
reservations:
|
||||
memory: 512M
|
||||
networks:
|
||||
- docker-dataplane
|
||||
|
||||
networks:
|
||||
docker-dataplane:
|
||||
external: true
|
||||
name: docker-dataplane
|
||||
```
|
||||
|
||||
### Phase 5: Environment Variables
|
||||
|
||||
Add to Portainer stack environment or `.env`:
|
||||
|
||||
```bash
|
||||
# Generate these:
|
||||
PAPERLESS_DB_PASSWORD=<openssl rand -hex 32>
|
||||
PAPERLESS_SECRET_KEY=<openssl rand -base64 32>
|
||||
PAPERLESS_ADMIN_PASSWORD=<your-admin-password>
|
||||
```
|
||||
|
||||
### Phase 6: Deploy Stack
|
||||
|
||||
Via Portainer or:
|
||||
```bash
|
||||
cd /mnt/media/Projects/portainer-core/stacks
|
||||
docker-compose -f paperless.yml up -d
|
||||
```
|
||||
|
||||
### Phase 7: Configure NPM Proxy
|
||||
|
||||
Create proxy host in NPM:
|
||||
- Domain: `documents.schweitz.net`
|
||||
- Forward: `paperless:8000` (or `192.168.86.149:8091`)
|
||||
- SSL: Request Let's Encrypt certificate
|
||||
- Websockets: Enable
|
||||
- Access List: As needed (Authentik SSO optional)
|
||||
|
||||
### Phase 8: Post-Deployment Configuration
|
||||
|
||||
1. **Access Paperless UI:** https://documents.schweitz.net
|
||||
2. **Login** with admin credentials
|
||||
3. **Create API Token:** My Profile → API Token → Create
|
||||
4. **Create Custom Fields:**
|
||||
- `source_url` (URL)
|
||||
- `library_indexed` (Boolean)
|
||||
- `library_doc_id` (Text)
|
||||
- `collection` (Text)
|
||||
|
||||
5. **Configure Webhook Workflow:**
|
||||
- Trigger: Document Added
|
||||
- Action: Webhook to `http://library-desk:8089/documents/webhook`
|
||||
|
||||
### Phase 9: Update Library Desk Configuration
|
||||
|
||||
Add to Library Desk environment:
|
||||
```bash
|
||||
PAPERLESS_URL=http://paperless:8000
|
||||
PAPERLESS_TOKEN=<api-token>
|
||||
CLAMAV_HOST=192.168.86.149 # Host IP (ClamAV on host)
|
||||
CLAMAV_PORT=3310
|
||||
CLAMAV_ENABLED=true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files to Create/Modify
|
||||
|
||||
| Action | File | Description |
|
||||
|--------|------|-------------|
|
||||
| Create | `stacks/paperless.yml` | Paperless-ngx stack |
|
||||
| Update | `CONTAINERS.md` | Add Paperless profile, update tables |
|
||||
| Update | `stacks/postgres-shared.yml` | Add paperless DB setup in comments |
|
||||
| Update | `stacks/redis-shared.yml` | Update DB allocation (DB 8: Paperless) |
|
||||
|
||||
## CONTAINERS.md Updates
|
||||
|
||||
Add to Quick Reference table:
|
||||
```
|
||||
| **Paperless-ngx** | 8091 | https://documents.schweitz.net | Internet | No | 8 | ✅ Running |
|
||||
| **ClamAV** | 3310 | N/A (host service) | No | No | - | ✅ Running |
|
||||
```
|
||||
|
||||
Add to External Domains:
|
||||
```
|
||||
- **documents.schweitz.net** → Paperless-ngx
|
||||
```
|
||||
|
||||
Update Redis DB allocation:
|
||||
```
|
||||
DB 8: Paperless (task queue, cache)
|
||||
```
|
||||
|
||||
Add to PostgreSQL databases:
|
||||
```
|
||||
paperless (document storage)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- [ ] ClamAV responds: `clamdscan --ping 3`
|
||||
- [ ] PostgreSQL database exists: `docker exec postgres-shared psql -U postgres -c '\l' | grep paperless`
|
||||
- [ ] Paperless container running: `docker ps | grep paperless`
|
||||
- [ ] Paperless health check passes
|
||||
- [ ] External access works: https://documents.schweitz.net
|
||||
- [ ] Library Desk can reach Paperless webhook endpoint
|
||||
- [ ] ClamAV accessible from containers: `nc -zv 192.168.86.149 3310`
|
||||
+196
-2
@@ -259,5 +259,199 @@ If already logged into another `.schweitz.net` service:
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-12-13
|
||||
**Based on**: library.schweitz.net setup session
|
||||
## Appendix: Portainer API for Remote Stack Deployment
|
||||
|
||||
When you cannot access the Portainer web UI (e.g., outside home network), you can deploy stacks via the API.
|
||||
|
||||
### Authentication
|
||||
|
||||
```bash
|
||||
# Get JWT token (valid for 8 hours)
|
||||
http --ignore-stdin POST http://192.168.86.149:8001/api/auth \
|
||||
username=admin password=<password>
|
||||
|
||||
# Response: {"jwt":"eyJ..."}
|
||||
```
|
||||
|
||||
### List Stacks
|
||||
|
||||
```bash
|
||||
http --ignore-stdin GET http://192.168.86.149:8001/api/stacks \
|
||||
"Authorization:Bearer <token>"
|
||||
```
|
||||
|
||||
### Read Stack File
|
||||
|
||||
```bash
|
||||
http --ignore-stdin GET "http://192.168.86.149:8001/api/stacks/<stack_id>/file" \
|
||||
"Authorization:Bearer <token>"
|
||||
```
|
||||
|
||||
### Create Stack
|
||||
|
||||
**Endpoint**: `POST /api/stacks/create/standalone/string?endpointId=3`
|
||||
|
||||
**Payload format**:
|
||||
```json
|
||||
{
|
||||
"name": "stack-name",
|
||||
"stackFileContent": "version: '3.8'\nservices:\n ...",
|
||||
"env": [
|
||||
{"name": "VAR_NAME", "value": "var_value"},
|
||||
{"name": "SECRET_KEY", "value": "secret_value"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Example deployment script**:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Get token
|
||||
TOKEN=$(http --ignore-stdin POST http://192.168.86.149:8001/api/auth \
|
||||
username=admin password=<password> | jq -r '.jwt')
|
||||
|
||||
# Read stack file and create JSON payload
|
||||
STACK_CONTENT=$(cat /path/to/stack.yml)
|
||||
jq -n \
|
||||
--arg name "my-stack" \
|
||||
--arg content "$STACK_CONTENT" \
|
||||
'{
|
||||
name: $name,
|
||||
stackFileContent: $content,
|
||||
env: [
|
||||
{name: "DB_PASSWORD", value: "secret123"},
|
||||
{name: "API_KEY", value: "key456"}
|
||||
]
|
||||
}' > /tmp/payload.json
|
||||
|
||||
# Deploy
|
||||
curl -s -X POST "http://192.168.86.149:8001/api/stacks/create/standalone/string?endpointId=3" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @/tmp/payload.json
|
||||
```
|
||||
|
||||
### Update Stack
|
||||
|
||||
```bash
|
||||
curl -s -X PUT "http://192.168.86.149:8001/api/stacks/<stack_id>?endpointId=3" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @/tmp/payload.json
|
||||
```
|
||||
|
||||
### Delete Stack
|
||||
|
||||
```bash
|
||||
http --ignore-stdin DELETE "http://192.168.86.149:8001/api/stacks/<stack_id>?endpointId=3" \
|
||||
"Authorization:Bearer <token>"
|
||||
```
|
||||
|
||||
### Key Notes
|
||||
|
||||
- **Endpoint ID**: Use `3` for the local Docker environment (verify with `GET /api/endpoints`)
|
||||
- **Stack Type**: Use `standalone/string` for Docker Compose stacks (not Swarm)
|
||||
- **Environment Variables**: Passed as array of `{name, value}` objects, referenced in compose as `${VAR_NAME}`
|
||||
- **Token Expiry**: JWT tokens expire after 8 hours; re-authenticate if needed
|
||||
|
||||
---
|
||||
|
||||
## Appendix: NPM API for Remote Proxy Configuration
|
||||
|
||||
When you cannot access the NPM web UI, you can configure proxy hosts via the API (port 81).
|
||||
|
||||
### Authentication
|
||||
|
||||
```bash
|
||||
http --ignore-stdin POST http://192.168.86.149:81/api/tokens \
|
||||
identity=<email> secret=<password>
|
||||
|
||||
# Response: {"token":"eyJ...", "expires":"..."}
|
||||
```
|
||||
|
||||
### List Proxy Hosts
|
||||
|
||||
```bash
|
||||
http --ignore-stdin GET http://192.168.86.149:81/api/nginx/proxy-hosts \
|
||||
"Authorization:Bearer <token>"
|
||||
```
|
||||
|
||||
### Create Proxy Host (without SSL)
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://192.168.86.149:81/api/nginx/proxy-hosts \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"domain_names": ["subdomain.schweitz.net"],
|
||||
"forward_scheme": "http",
|
||||
"forward_host": "192.168.86.149",
|
||||
"forward_port": 8091,
|
||||
"access_list_id": 0,
|
||||
"certificate_id": 0,
|
||||
"ssl_forced": false,
|
||||
"caching_enabled": false,
|
||||
"block_exploits": true,
|
||||
"advanced_config": "",
|
||||
"allow_websocket_upgrade": true,
|
||||
"http2_support": false,
|
||||
"hsts_enabled": false,
|
||||
"hsts_subdomains": false,
|
||||
"enabled": true,
|
||||
"locations": []
|
||||
}'
|
||||
```
|
||||
|
||||
### Request Let's Encrypt Certificate
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://192.168.86.149:81/api/nginx/certificates \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"domain_names": ["subdomain.schweitz.net"],
|
||||
"meta": {"dns_challenge": false},
|
||||
"provider": "letsencrypt"
|
||||
}'
|
||||
|
||||
# Response includes certificate ID
|
||||
```
|
||||
|
||||
### Update Proxy Host with SSL
|
||||
|
||||
```bash
|
||||
curl -s -X PUT http://192.168.86.149:81/api/nginx/proxy-hosts/<proxy_id> \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"domain_names": ["subdomain.schweitz.net"],
|
||||
"forward_scheme": "http",
|
||||
"forward_host": "192.168.86.149",
|
||||
"forward_port": 8091,
|
||||
"access_list_id": 0,
|
||||
"certificate_id": <cert_id>,
|
||||
"ssl_forced": true,
|
||||
"caching_enabled": false,
|
||||
"block_exploits": true,
|
||||
"advanced_config": "",
|
||||
"allow_websocket_upgrade": true,
|
||||
"http2_support": true,
|
||||
"hsts_enabled": true,
|
||||
"hsts_subdomains": false,
|
||||
"enabled": true,
|
||||
"locations": []
|
||||
}'
|
||||
```
|
||||
|
||||
### Key Notes
|
||||
|
||||
- **API Port**: NPM API is on port 81, not 80/443
|
||||
- **Token Expiry**: Tokens expire after 1 day
|
||||
- **SSL Flow**: Create proxy host → Request certificate → Update proxy host with certificate_id
|
||||
- **forward_host**: Use container name (if on same network) or host IP
|
||||
- **Verify**: Check `meta.nginx_online: true` in response
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-12-24
|
||||
**Based on**: library.schweitz.net setup session, Paperless-ngx API deployment
|
||||
|
||||
@@ -35,6 +35,10 @@ services:
|
||||
# SearXNG Configuration
|
||||
- SEARXNG_URL=http://searxng:8080
|
||||
|
||||
# Paperless Configuration
|
||||
- PAPERLESS_URL=http://paperless:8000
|
||||
- PAPERLESS_TOKEN=${PAPERLESS_TOKEN}
|
||||
|
||||
# Ollama Configuration (for embeddings)
|
||||
- OLLAMA_URL=http://ollama:11434
|
||||
- OLLAMA_MODEL=nomic-embed-text
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
version: '3.8'
|
||||
|
||||
# Paperless-ngx - Document Management System
|
||||
# Port: 8091 (HTTP)
|
||||
# GPU: No
|
||||
# External: documents.schweitz.net
|
||||
# Storage: Configs on SSD (backed up), documents on HDD
|
||||
|
||||
services:
|
||||
paperless:
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
container_name: paperless
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8091:8000"
|
||||
volumes:
|
||||
# SSD - configs and database (backed up)
|
||||
- /home/jpmschweitzer/docker-data/paperless/data:/usr/src/paperless/data
|
||||
# HDD - document storage
|
||||
- /mnt/media/paperless/media:/usr/src/paperless/media
|
||||
- /mnt/media/paperless/consume:/usr/src/paperless/consume
|
||||
- /mnt/media/paperless/export:/usr/src/paperless/export
|
||||
environment:
|
||||
# Database (shared PostgreSQL)
|
||||
PAPERLESS_DBENGINE: postgresql
|
||||
PAPERLESS_DBHOST: postgres-shared
|
||||
PAPERLESS_DBPORT: 5432
|
||||
PAPERLESS_DBNAME: paperless
|
||||
PAPERLESS_DBUSER: paperless_user
|
||||
PAPERLESS_DBPASS: ${PAPERLESS_DB_PASSWORD}
|
||||
|
||||
# Redis (shared, DB 8)
|
||||
PAPERLESS_REDIS: redis://redis-shared:6379/8
|
||||
|
||||
# Security
|
||||
PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}
|
||||
|
||||
# URLs
|
||||
PAPERLESS_URL: https://documents.schweitz.net
|
||||
PAPERLESS_ALLOWED_HOSTS: "*"
|
||||
PAPERLESS_CORS_ALLOWED_HOSTS: "http://localhost:8091,https://documents.schweitz.net"
|
||||
|
||||
# OCR Settings
|
||||
PAPERLESS_OCR_LANGUAGE: eng
|
||||
PAPERLESS_OCR_LANGUAGES: nld # Install Dutch on first startup
|
||||
PAPERLESS_OCR_MODE: skip
|
||||
PAPERLESS_OCR_OUTPUT_TYPE: pdfa
|
||||
|
||||
# Webhooks (for Library Desk integration)
|
||||
PAPERLESS_WEBHOOKS_ALLOW_INTERNAL_REQUESTS: "true"
|
||||
|
||||
# Admin user (created on first run)
|
||||
PAPERLESS_ADMIN_USER: admin
|
||||
PAPERLESS_ADMIN_PASSWORD: ${PAPERLESS_ADMIN_PASSWORD}
|
||||
|
||||
# Timezone
|
||||
TZ: Europe/Amsterdam
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 4G
|
||||
reservations:
|
||||
memory: 512M
|
||||
networks:
|
||||
- docker-dataplane
|
||||
|
||||
networks:
|
||||
docker-dataplane:
|
||||
external: true
|
||||
name: docker-dataplane
|
||||
|
||||
# =============================================================================
|
||||
# DEPLOYMENT INSTRUCTIONS
|
||||
# =============================================================================
|
||||
#
|
||||
# 1. Environment variables required in Portainer:
|
||||
# PAPERLESS_DB_PASSWORD=<openssl rand -hex 32>
|
||||
# PAPERLESS_SECRET_KEY=<openssl rand -base64 32>
|
||||
# PAPERLESS_ADMIN_PASSWORD=<your-admin-password>
|
||||
#
|
||||
# 2. Database already created in postgres-shared:
|
||||
# Database: paperless
|
||||
# User: paperless_user
|
||||
#
|
||||
# 3. Redis using shared instance DB 8
|
||||
#
|
||||
# 4. After deployment, configure NPM:
|
||||
# Domain: documents.schweitz.net
|
||||
# Forward: paperless:8000 or 192.168.86.149:8091
|
||||
# SSL: Let's Encrypt
|
||||
# Websockets: Enable
|
||||
#
|
||||
# 5. Post-deployment in Paperless UI:
|
||||
# - Create API token (My Profile → API Token)
|
||||
# - Create custom fields: source_url, library_indexed, library_doc_id, collection
|
||||
# - Create webhook workflow to http://library-desk:8089/documents/webhook
|
||||
#
|
||||
# 6. ClamAV is running on host at 192.168.86.149:3310
|
||||
# Configure Library Desk with:
|
||||
# CLAMAV_HOST=192.168.86.149
|
||||
# CLAMAV_PORT=3310
|
||||
# CLAMAV_ENABLED=true
|
||||
@@ -133,4 +133,5 @@ networks:
|
||||
# - Authentik (identity provider)
|
||||
# - Gitea (git hosting) - migrated from dedicated instance
|
||||
# - Nextcloud (personal cloud storage) - migrated from MariaDB
|
||||
# - Paperless-ngx (document management) - DB: paperless, User: paperless_user
|
||||
# - Future applications as needed
|
||||
|
||||
+10
-5
@@ -63,11 +63,16 @@ networks:
|
||||
#
|
||||
# Redis supports 16 databases (0-15). Assign one per application:
|
||||
#
|
||||
# DB 0: General cache (default, shared lightweight caching)
|
||||
# DB 1: Authentik (sessions, cache, message queue)
|
||||
# DB 2: Gitea (cache, sessions)
|
||||
# DB 3: Nextcloud (file locking, distributed cache, sessions)
|
||||
# DB 4-15: Reserved for future applications
|
||||
# DB 0: Authentik (sessions, cache, message queue)
|
||||
# DB 1: Tatlock (memory)
|
||||
# DB 2: Wiki.js
|
||||
# DB 3: Scheduler
|
||||
# DB 4: Library Desk
|
||||
# DB 5: SearXNG
|
||||
# DB 6: Tatlock (benchmarks)
|
||||
# DB 7: Nextcloud (file locking, distributed cache, sessions)
|
||||
# DB 8: Paperless (task queue, cache)
|
||||
# DB 9-15: Reserved for future applications
|
||||
#
|
||||
# Connection Examples:
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user