- 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>
7.2 KiB
7.2 KiB
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:
# 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
# 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
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
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:
# 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:
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(or192.168.86.149:8091) - SSL: Request Let's Encrypt certificate
- Websockets: Enable
- Access List: As needed (Authentik SSO optional)
Phase 8: Post-Deployment Configuration
-
Access Paperless UI: https://documents.schweitz.net
-
Login with admin credentials
-
Create API Token: My Profile → API Token → Create
-
Create Custom Fields:
source_url(URL)library_indexed(Boolean)library_doc_id(Text)collection(Text)
-
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:
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