2026-07-06 22:09:36 +02:00
|
|
|
|
# Paperless OCR worker (SOLARIA) — KB module 3.
|
|
|
|
|
|
#
|
|
|
|
|
|
# Same image as services/paperless/ but the command is overridden to run ONLY
|
|
|
|
|
|
# a celery worker (no webserver, no consumer, no beat). It attaches to the
|
|
|
|
|
|
# broker/DB of paperless@PIHA and sees the document storage via NFS from PIHA.
|
|
|
|
|
|
# This split is NOT officially supported by paperless-ngx but is the
|
|
|
|
|
|
# maintainer-confirmed pattern (GH discussion #3900); constraints and risks
|
|
|
|
|
|
# are documented in README.md.
|
|
|
|
|
|
#
|
|
|
|
|
|
# When SOLARIA is powered down this worker simply disappears — queued tasks
|
|
|
|
|
|
# wait in Redis@PIHA (AOF-persisted) and PIHA's built-in concurrency-1 worker
|
|
|
|
|
|
# grinds slowly. When SOLARIA returns, this worker drains the queue.
|
|
|
|
|
|
services:
|
|
|
|
|
|
paperless-worker:
|
|
|
|
|
|
# MUST be the exact same tag as services/paperless/docker-compose.yml —
|
|
|
|
|
|
# shared DB schema + celery task signatures. Bump both together.
|
|
|
|
|
|
image: ghcr.io/paperless-ngx/paperless-ngx:2.14
|
|
|
|
|
|
container_name: paperless-worker
|
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
|
# Worker-only mode: the image entrypoint handles USERMAP + language
|
|
|
|
|
|
# install, then execs this instead of the full s6 service tree.
|
|
|
|
|
|
# OCR (ocrmypdf/tesseract) is CPU-bound — the win here is SOLARIA's
|
|
|
|
|
|
# 24 cores, not the GPU (stock paperless OCR does not use CUDA).
|
2026-07-12 20:51:04 +02:00
|
|
|
|
#
|
|
|
|
|
|
# MUST be an absolute path. /sbin/docker-entrypoint.sh branches on
|
|
|
|
|
|
# argv[0]: anything NOT starting with "/" is treated as a Django
|
|
|
|
|
|
# management-command name and handed to `manage.py` (`exec gosu paperless
|
|
|
|
|
|
# python3 manage.py "$@"`) — so a bare `celery ...` command fails with
|
|
|
|
|
|
# "Unknown command: 'celery'". An absolute path takes the `else exec "$@"`
|
|
|
|
|
|
# branch instead, running celery directly. `gosu paperless` in front
|
|
|
|
|
|
# replaces the implicit gosu the manage.py branch would have applied, so
|
|
|
|
|
|
# the process still runs as uid 1000 (paperless), not root — required so
|
|
|
|
|
|
# files it writes on the NFS-shared storage keep the correct owner and
|
|
|
|
|
|
# match USERMAP_UID/GID below. Verified against the image: gosu is at
|
|
|
|
|
|
# /usr/sbin/gosu, celery at /usr/local/bin/celery.
|
|
|
|
|
|
command: /usr/sbin/gosu paperless /usr/local/bin/celery --app paperless worker --loglevel INFO --concurrency ${WORKER_CONCURRENCY:-4}
|
2026-07-06 22:09:36 +02:00
|
|
|
|
env_file:
|
|
|
|
|
|
- .env
|
|
|
|
|
|
environment:
|
|
|
|
|
|
# Broker + DB on PIHA over the fast LAN (NOT Tailscale) — same L2 as
|
feat(kb): configi Paperless/Nextcloud wg 9 decyzji — NC na PIHA, domeny kapala, Redis requirepass, backup SOLARIA, NC pin 34
Co zrobione:
- Nextcloud host = PIHA (always-on dla aktywnego uzycia), owner_node +
LAN_BIND_IP/TRUSTED_PROXIES w .env, README zaktualizowane
- Redis brokera Paperlessa: requirepass, PAPERLESS_REDIS_PASSWORD w .env
po obu stronach (PIHA + worker@SOLARIA), healthchecki z auth
- Domeny potwierdzone: paper.kapala.org, cloud.kapala.org (Cloudflare
DNS-only -> Tailscale PIHA, wildcard cert juz pokrywa) — udokumentowane,
nic nie utworzone
- Backup Paperlessa zatwierdzony: document_exporter + rsync/borg -> SOLARIA,
retencja 7/4/6, offsite jako future-note
- Nextcloud pin: 34-apache (zweryfikowany aktualny stable, endoflife.date)
- Whoosh fallback-worker: zaakceptowane bez zmian
- Porty/wylaczenie local login/sizing OCR-workera: przeniesione z "decyzji"
na "TODO przy deployu"
- DECYZJE-do-podjecia.md zaktualizowane: wszystko poza portami/loginem/
sizingiem przeniesione do "Rozstrzygniete"
Tylko edycja configow w repo — nic nie zdeployowane, zadne kontenery nie
byly ruszane, DNS/vhosty nie utworzone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 16:15:35 +02:00
|
|
|
|
# the NFS mounts, transfer is not the bottleneck. Broker has
|
|
|
|
|
|
# requirepass set — PAPERLESS_REDIS_PASSWORD in .env MUST equal the
|
|
|
|
|
|
# value in services/paperless/.env on PIHA.
|
|
|
|
|
|
- PAPERLESS_REDIS=redis://:${PAPERLESS_REDIS_PASSWORD}@192.168.31.5:6380
|
2026-07-06 22:09:36 +02:00
|
|
|
|
- PAPERLESS_DBHOST=192.168.31.5
|
|
|
|
|
|
- PAPERLESS_DBPORT=5434
|
|
|
|
|
|
- PAPERLESS_DBNAME=paperless
|
|
|
|
|
|
- PAPERLESS_DBUSER=paperless
|
|
|
|
|
|
# Settings below MUST mirror services/paperless — drift between the two
|
|
|
|
|
|
# stacks changes how documents get named/OCR-ed depending on which host
|
|
|
|
|
|
# picked the task.
|
|
|
|
|
|
- PAPERLESS_URL=https://paper.kapala.org
|
|
|
|
|
|
- PAPERLESS_TIME_ZONE=Europe/Warsaw
|
|
|
|
|
|
- PAPERLESS_OCR_LANGUAGES=pol
|
|
|
|
|
|
- PAPERLESS_OCR_LANGUAGE=pol+eng
|
|
|
|
|
|
# Threads used INSIDE a single OCR task; total load ~ concurrency ×
|
|
|
|
|
|
# threads. 4×4=16 threads leaves headroom on 24 cores/32 threads.
|
feat(kb): configi Paperless/Nextcloud wg 9 decyzji — NC na PIHA, domeny kapala, Redis requirepass, backup SOLARIA, NC pin 34
Co zrobione:
- Nextcloud host = PIHA (always-on dla aktywnego uzycia), owner_node +
LAN_BIND_IP/TRUSTED_PROXIES w .env, README zaktualizowane
- Redis brokera Paperlessa: requirepass, PAPERLESS_REDIS_PASSWORD w .env
po obu stronach (PIHA + worker@SOLARIA), healthchecki z auth
- Domeny potwierdzone: paper.kapala.org, cloud.kapala.org (Cloudflare
DNS-only -> Tailscale PIHA, wildcard cert juz pokrywa) — udokumentowane,
nic nie utworzone
- Backup Paperlessa zatwierdzony: document_exporter + rsync/borg -> SOLARIA,
retencja 7/4/6, offsite jako future-note
- Nextcloud pin: 34-apache (zweryfikowany aktualny stable, endoflife.date)
- Whoosh fallback-worker: zaakceptowane bez zmian
- Porty/wylaczenie local login/sizing OCR-workera: przeniesione z "decyzji"
na "TODO przy deployu"
- DECYZJE-do-podjecia.md zaktualizowane: wszystko poza portami/loginem/
sizingiem przeniesione do "Rozstrzygniete"
Tylko edycja configow w repo — nic nie zdeployowane, zadne kontenery nie
byly ruszane, DNS/vhosty nie utworzone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 16:15:35 +02:00
|
|
|
|
# TODO AT DEPLOY (module 5): measure on a sample before the 70k-attachment
|
|
|
|
|
|
# batch — decide whether to raise concurrency (e.g. 8) or keep headroom
|
|
|
|
|
|
# for ollama/AI workloads.
|
2026-07-06 22:09:36 +02:00
|
|
|
|
- PAPERLESS_THREADS_PER_WORKER=4
|
|
|
|
|
|
# MUST match paperless@PIHA — files on the NFS export carry numeric
|
|
|
|
|
|
# UID/GID; a mismatch means this worker cannot read/write documents.
|
|
|
|
|
|
- USERMAP_UID=1000
|
|
|
|
|
|
- USERMAP_GID=1000
|
|
|
|
|
|
volumes:
|
|
|
|
|
|
# NFS volumes from PIHA (defined below). Container paths MUST be
|
|
|
|
|
|
# identical to paperless@PIHA — task payloads and the DB carry absolute
|
|
|
|
|
|
# /usr/src/paperless/... paths.
|
|
|
|
|
|
- paperless_data:/usr/src/paperless/data
|
|
|
|
|
|
- paperless_media:/usr/src/paperless/media
|
|
|
|
|
|
- paperless_consume:/usr/src/paperless/consume
|
2026-07-12 20:51:04 +02:00
|
|
|
|
# SCRATCH_DIR (default /tmp/paperless — paperless-ngx sets it to
|
|
|
|
|
|
# tempfile.gettempdir()/"paperless" when PAPERLESS_SCRATCH_DIR is unset,
|
|
|
|
|
|
# see src/paperless/settings.py) is where paperless@PIHA stages the
|
|
|
|
|
|
# uploaded file before consuming it. The celery task payload carries
|
|
|
|
|
|
# that path as an ABSOLUTE path, and this worker opens the same path on
|
|
|
|
|
|
# its own filesystem — without a shared mount here, any task picked up
|
|
|
|
|
|
# by SOLARIA (instead of the local PIHA worker) fails with "Cannot
|
|
|
|
|
|
# consume ...: File not found". Must be NFS-mounted at the identical
|
|
|
|
|
|
# container path, same reasoning as data/media/consume above. No need
|
|
|
|
|
|
# to set PAPERLESS_SCRATCH_DIR explicitly: the default already resolves
|
|
|
|
|
|
# to /tmp/paperless on both sides.
|
|
|
|
|
|
- paperless_scratch:/tmp/paperless
|
2026-07-06 22:09:36 +02:00
|
|
|
|
healthcheck:
|
|
|
|
|
|
# Pings THIS worker through the broker — proves broker connectivity and
|
|
|
|
|
|
# a live celery process in one shot.
|
|
|
|
|
|
test: ["CMD", "celery", "--app", "paperless", "inspect", "ping", "--timeout", "10"]
|
|
|
|
|
|
interval: 60s
|
|
|
|
|
|
timeout: 15s
|
|
|
|
|
|
retries: 3
|
|
|
|
|
|
start_period: 60s
|
|
|
|
|
|
|
|
|
|
|
|
# Docker-managed NFS mounts (no /etc/fstab entry needed on SOLARIA). Docker
|
|
|
|
|
|
# mounts these lazily on container start; if PIHA is unreachable the container
|
|
|
|
|
|
# fails to start and docker retries via restart policy — acceptable, the queue
|
|
|
|
|
|
# waits on PIHA anyway. The HOST-side prerequisite is on PIHA: the /etc/exports
|
|
|
|
|
|
# entry (see README.md, "NFS export").
|
|
|
|
|
|
volumes:
|
|
|
|
|
|
paperless_data:
|
|
|
|
|
|
driver: local
|
|
|
|
|
|
driver_opts:
|
|
|
|
|
|
type: nfs
|
|
|
|
|
|
o: addr=192.168.31.5,rw,nfsvers=4.1,hard,timeo=150
|
|
|
|
|
|
device: ":/opt/homelab/data/paperless/data"
|
|
|
|
|
|
paperless_media:
|
|
|
|
|
|
driver: local
|
|
|
|
|
|
driver_opts:
|
|
|
|
|
|
type: nfs
|
|
|
|
|
|
o: addr=192.168.31.5,rw,nfsvers=4.1,hard,timeo=150
|
|
|
|
|
|
device: ":/opt/homelab/data/paperless/media"
|
|
|
|
|
|
paperless_consume:
|
|
|
|
|
|
driver: local
|
|
|
|
|
|
driver_opts:
|
|
|
|
|
|
type: nfs
|
|
|
|
|
|
o: addr=192.168.31.5,rw,nfsvers=4.1,hard,timeo=150
|
|
|
|
|
|
device: ":/opt/homelab/data/paperless/consume"
|
2026-07-12 20:51:04 +02:00
|
|
|
|
paperless_scratch:
|
|
|
|
|
|
driver: local
|
|
|
|
|
|
driver_opts:
|
|
|
|
|
|
type: nfs
|
|
|
|
|
|
o: addr=192.168.31.5,rw,nfsvers=4.1,hard,timeo=150
|
|
|
|
|
|
device: ":/opt/homelab/data/paperless/scratch"
|