If you've got a pile of PDFs, scanned invoices, insurance docs, and tax records living in a random folder hierarchy on your NAS — or worse, in Google Drive — Paperless-ngx is the fix. It's an open-source document management system that ingests documents, runs OCR on everything, and makes the entire archive full-text searchable. I've been running it on Proxmox LXC for over a year, and it's one of those services I'd rebuild immediately if my home lab burned down.
This guide covers the complete 2026 setup: LXC container, Docker Compose stack, storage passthrough, scanner integration, and a few production-level tweaks that the official docs gloss over.
What Is Paperless-ngx and Why Should You Self-Host It?
Paperless-ngx is a fork of Paperless and Paperless-ng that's actively maintained and genuinely production-ready. Drop a PDF in the consume folder — from a network scanner, email, or the mobile app — and within minutes it's OCR'd, tagged, assigned a correspondent, and searchable by full text.
The killer feature is the AI-assisted auto-tagging: you train it over time, and it starts classifying documents automatically. After a few hundred documents, the classifier is accurate enough that I rarely need to touch incoming docs manually.
Running Paperless-ngx in your Proxmox home lab instead of a cloud service means zero per-document fees, your documents never leave your network, and the archive survives any SaaS shutdown.
Prerequisites
Before you start, you need:
Step 1: How Do I Create the Proxmox LXC Container?
Download the Ubuntu 22.04 LXC template if you haven't already:
pveam update
pveam download local ubuntu-22.04-standard_22.04-1_amd64.tar.zst
Create the container. I use 2 vCPUs and 3 GB RAM — OCR is CPU-intensive when it first processes a backlog:
pct create 110 local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst \
--hostname paperless \
--memory 3072 \
--cores 2 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--storage local-lvm \
--rootfs local-lvm:8 \
--unprivileged 1 \
--features nesting=1
The nesting=1 feature flag is required for Docker to run inside an unprivileged LXC container. Start it and grab the IP:
pct start 110
pct exec 110 -- ip addr show eth0 | grep "inet "
Set a static IP through your router or OPNsense DHCP reservation. Assign something memorable — 192.168.x.110 works well.
Step 2: Install Docker Inside the LXC
pct exec 110 -- bash -c "apt-get update && apt-get install -y ca-certificates curl gnupg"
pct exec 110 -- bash -c "install -m 0755 -d /etc/apt/keyrings && \
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
chmod a+r /etc/apt/keyrings/docker.gpg"
pct exec 110 -- bash -c 'echo \
"deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list'
pct exec 110 -- bash -c "apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin"
Verify it works:
pct exec 110 -- docker run --rm hello-world
Step 3: How Do I Set Up Storage Passthrough for the Document Archive?
Don't store your document archive inside the LXC rootfs — put it on a proper storage volume. I use a ZFS dataset on my main Proxmox node.
On the Proxmox host, create the dataset and directories:
zfs create rpool/paperless
mkdir -p /rpool/paperless/{data,media,export,consume}
chown -R 1000:1000 /rpool/paperless
Bind-mount them into the LXC by adding to /etc/pve/lxc/110.conf:
mp0: /rpool/paperless,mp=/paperless
Restart the container:
pct restart 110
Inside the LXC, verify the mount:
pct exec 110 -- ls /paperless
# should show: consume data export media
If you're storing documents on a NAS drive instead of a local ZFS pool, the Seagate IronWolf 4TB and WD Red Plus 4TB are both rated for NAS workloads and significantly more reliable than desktop drives under continuous write pressure. For a more budget-conscious option, a Samsung 870 EVO 2TB SSD as the archive drive gives you excellent random read speed when searching.
Step 4: Configure the Docker Compose Stack
Inside the LXC, create /opt/paperless/docker-compose.yml:
mkdir -p /opt/paperless cat > /opt/paperless/docker-compose.yml << 'EOF' version: "3.4"services: broker: image: docker.io/library/redis:7 restart: unless-stopped volumes: - redisdata:/data
db: image: docker.io/library/postgres:16 restart: unless-stopped volumes: - pgdata:/var/lib/postgresql/data environment: POSTGRES_DB: paperless POSTGRES_USER: paperless POSTGRES_PASSWORD: changeme_strong_password
webserver: image: ghcr.io/paperless-ngx/paperless-ngx:latest restart: unless-stopped depends_on: - db - broker ports: - "8000:8000" volumes: - /paperless/data:/usr/src/paperless/data - /paperless/media:/usr/src/paperless/media - /paperless/export:/usr/src/paperless/export - /paperless/consume:/usr/src/paperless/consume environment: PAPERLESS_REDIS: redis://broker:6379 PAPERLESS_DBHOST: db PAPERLESS_DBUSER: paperless PAPERLESS_DBPASS: changeme_strong_password PAPERLESS_OCR_LANGUAGE: eng PAPERLESS_SECRET_KEY: changeme_generate_with_openssl_rand_-hex_32 PAPERLESS_TIME_ZONE: America/New_York PAPERLESS_URL: http://192.168.1.110:8000 PAPERLESS_OCR_THREADS: 2 PAPERLESS_CONSUMER_POLLING: 60 PAPERLESS_TASK_WORKERS: 2 PAPERLESS_THREADS_PER_WORKER: 1
volumes: pgdata: redisdata: EOF
Adjust PAPERLESS_OCR_LANGUAGE for your document corpus — use eng+spa for English + Spanish.
Generate a real secret key:
pct exec 110 -- bash -c "docker run --rm -it ghcr.io/paperless-ngx/paperless-ngx:latest python3 -c 'import secrets; print(secrets.token_hex(32))'"
Spin up the stack:
pct exec 110 -- bash -c "cd /opt/paperless && docker compose up -d"
Create the admin user:
pct exec 110 -- bash -c "cd /opt/paperless && docker compose exec webserver python3 manage.py createsuperuser"
Hit http://192.168.1.110:8000 in your browser and you should see the Paperless-ngx UI.
Step 5: How Do I Set Up Scanner Integration?
Network Scanners
If you have a network-capable scanner, point it at the consume directory directly or expose it via Samba:
pct exec 110 -- bash -c "apt-get install -y samba" cat >> /etc/samba/smb.conf << 'EOF'
[paperless-consume] path = /paperless/consume browseable = yes read only = no guest ok = no valid users = paperless EOF pct exec 110 -- bash -c "smbpasswd -a paperless && systemctl restart smbd"
For scanning hardware, the Fujitsu ScanSnap iX1600 is the gold standard for home office document scanning — duplex, 40ppm, and the companion app can push directly to a WiFi folder. The Canon imageFORMULA R40 is a solid mid-range pick if you're not ready to spend $400+. For lighter use, the Epson WorkForce ES-400 II handles typical home office volumes without complaints.
Mobile Scanning
Install the official Paperless-ngx mobile app (iOS/Android) or Paperless Mobile. Point it at your Paperless-ngx URL and admin credentials — you can snap a photo of a receipt and have it processed and tagged in under two minutes.
For remote access without opening ports, Cloudflare Tunnels in front of Paperless-ngx is the cleanest approach: zero exposed ports, free tier handles the traffic easily.
Step 6: How Does Auto-Classification Training Work?
Paperless-ngx's classifier needs documents to train on. Go to Admin → Machine Learning → Run trainer once you have 50+ documents ingested. After roughly 200 documents with properly assigned correspondents and tags, the auto-assignment accuracy becomes genuinely useful — I get around 90% accuracy on my setup.
Tag taxonomy matters. Keep it flat and broad rather than deep and specific: tax, insurance, medical, utilities, bank, receipts. Deep hierarchies defeat the classifier.
Step 7: Add HTTPS with Traefik
Don't run Paperless-ngx on HTTP in production, even on a local network. If you're already running Traefik on Proxmox LXC, add label-based routing to the compose file:
labels:
- "traefik.enable=true"
- "traefik.http.routers.paperless.rule=Host(paperless.yourdomain.internal)"
- "traefik.http.routers.paperless.tls=true"
- "traefik.http.routers.paperless.tls.certresolver=letsencrypt"
- "traefik.http.services.paperless.loadbalancer.server.port=8000"
Update PAPERLESS_URL in your compose file to the HTTPS hostname and restart.
Backup Strategy
Your document archive is irreplaceable. Paperless-ngx has a built-in export command:
docker compose exec webserver document_exporter /paperless/export
Add this to a cron job inside the LXC and sync the export directory to an external SSD or an offsite S3 bucket with rclone. I run nightly exports and sync to a Samsung T7 Shield 2TB connected to a separate machine.
For the media directory (the actual document files), use Proxmox Backup Server or ZFS snapshot replication. The media directory plus a PostgreSQL dump is your full recovery set:
docker compose exec db pg_dump -U paperless paperless > /paperless/export/paperless-db-$(date +%Y%m%d).sql
What Hardware Does Paperless-ngx Need?
On my home lab node (a Beelink GTi13 Ultra running Proxmox), the Paperless-ngx stack idles at roughly 180 MB RAM. During bulk OCR ingestion of a multi-page PDF, it spikes to 600–800 MB and pegs one CPU core. With 3 GB allocated and PAPERLESS_OCR_THREADS: 2, large batches finish without OOMing.
Anything running Proxmox comfortably handles Paperless-ngx as one LXC among many. A used Intel NUC 12 Pro or a refurbished Dell OptiPlex 7090 both have more than enough headroom to run Paperless alongside Immich, Jellyfin, and other services without breaking a sweat.
If you want to run the full stack plus ML-heavy auto-classification without throttling, add a second NVMe for the ZFS pool: the WD Black SN850X 2TB is what I run for fast random I/O on the media directory.
Caveats and Gotchas
OCR language packs add size. Each additional Tesseract language adds ~30–100 MB to the container image. Install only what you need.
PDF/A conversion. Paperless-ngx converts documents to PDF/A for long-term archival by default. This is correct behavior, but it can change file sizes significantly — a 2 MB scanned invoice might become 4 MB PDF/A. Plan storage accordingly.
Search re-indexing takes time. After version upgrades, Paperless often needs to rebuild the full-text search index. On a large archive (5,000+ docs), this can take 20–30 minutes. Don't restart the container mid-reindex.
LXC networking quirk. If the webserver container can't resolve broker or db by hostname, your Docker network inside the unprivileged LXC may have a DNS issue. Adding --dns 1.1.1.1 to the Docker compose network config usually fixes it.
Backlog ingestion tip. If you're ingesting a backlog of 1,000+ documents, drop them in 100 at a time. Dumping a thousand PDFs at once creates a task queue backlog that can cause Redis to consume unexpected memory.
Verdict
Paperless-ngx on Proxmox LXC is one of the most immediately useful self-hosted services you can run. Setup time is under an hour, and once the auto-classifier trains on your document corpus, it genuinely handles most of the sorting work. The combination of full-text OCR search, auto-tagging, and correspondent tracking turns a folder of scanned chaos into an archive you can actually query.
The only rough edge in 2026: the mobile scanning workflow is still slightly more friction than a Google Drive scan-to-folder flow. The mobile apps have improved, but it's not quite frictionless yet. That said, everything else about this stack — including the lack of any per-document fee or cloud dependency — more than makes up for it.
If you're already running Proxmox and want one more LXC workload that pays back daily, this is it.
> Disclosure: This post contains affiliate links. If you purchase through these links, I may earn a small commission at no extra cost to you.