homelab-blueprint/docker-compose-files/paperless-ngx/README.md
Brian Pooe cc6cdebba2 fix(paperless-ngx): keep SQLite database and index on local disk
Only media/consume/export/trash stay on the UNAS share; SQLite over
NFS risks lock contention and corruption.
2026-07-03 18:38:50 +02:00

116 lines
3.4 KiB
Markdown

# Paperless-ngx
Document management system that scans, indexes, and archives paper documents
with full-text OCR search. Runs with the bundled SQLite database and a Redis
task broker.
## Persistent Data
Split between local disk and the UNAS `paperless_data` share (see
[nfs-mounts](../../docs/unas/nfs-mounts.md)):
- `paperless_data` named volume (local disk) — SQLite database, search index,
and classification model. Kept off NFS because SQLite file locking over NFS
risks corruption and slow queries.
- Share dirs, NFS-mounted on the Proxmox host and bind-mounted into the LXC
at `PAPERLESS_DATA_ROOT`:
- `media/` — original and archived documents.
- `consume/` — watched for new documents; safe to empty once ingested.
- `export/` — target for `document_exporter` backups.
- `trash/` — deleted documents until emptied.
The share binds use `create_host_path: false`, so the stack refuses to start
if the share is not mounted. Redis broker state is in the
`paperless_redis_data` named volume.
## Installation
1. Create `.env` if it does not exist:
```bash
cp -n .env.sample .env
```
2. Set the required values in `.env`:
```dotenv
TZ=Africa/Johannesburg
PAPERLESS_URL=https://paperless-ngx.home.example.com
PAPERLESS_SECRET_KEY=<random-secret> # openssl rand -base64 48
PAPERLESS_DATA_ROOT=/mnt/unas/paperless_data
PUID=1000
PGID=1000
DOCKERLOGGING_MAXFILE=3
DOCKERLOGGING_MAXSIZE=10m
```
`PAPERLESS_URL` must match the URL used in the browser, otherwise CSRF
checks fail behind the reverse proxy.
3. Ensure the share is bind-mounted into the LXC and owned by the container
user (host-side UID for an unprivileged LXC is `LXC_IDMAP_BASE + PUID`):
```bash
# On the Proxmox host
pct set <vmid> -mp0 /mnt/unas/paperless_data,mp=/mnt/unas/paperless_data
mkdir -p /mnt/unas/paperless_data/{data,media,consume,export,trash}
chown -R 101000:101000 /mnt/unas/paperless_data
```
4. Deploy with `make`:
```bash
make deploy-paperless
```
Manual equivalent:
```bash
./substitute_env.sh docker-compose-files/paperless-ngx/template.yaml docker-compose.paperless.yml .env
docker compose -f docker-compose.paperless.yml config --quiet
docker compose -f docker-compose.paperless.yml up -d
```
5. Create the first user, unless `PAPERLESS_ADMIN_USER` is set in `.env`:
```bash
docker exec -it paperless createsuperuser
```
6. Verify the containers and open `http://<host-ip>:8000`:
```bash
docker compose -f docker-compose.paperless.yml ps
docker compose -f docker-compose.paperless.yml logs --tail=100 paperless
```
## Reverse Proxy
The Caddyfile proxies `paperless-ngx.<domain>` to this host on port 8000 with
`header_up Host {host}` so CSRF origin checks pass. After changing the port,
update `docker-compose-files/caddy/Caddyfile_template` and run
`make reload-caddy`.
## Backup
Run the built-in exporter, then back up `PAPERLESS_DATA_ROOT/export` (it
contains documents plus a manifest that restores tags, correspondents, and
metadata):
```bash
docker exec paperless document_exporter /usr/src/paperless/export --zip
```
## Useful Commands
```bash
# Tail logs
docker compose -f docker-compose.paperless.yml logs -f paperless
# Retrain the document classifier
docker exec paperless document_create_classifier
# Re-run OCR on a document
docker exec paperless document_archiver --overwrite --document <id>
```