Install Paperless-ngx on Proxmox VE
Why a Proxmox VM for Paperless-ngx
Paperless-ngx runs a multi-service stack (app server, PostgreSQL, Redis) and CPU-intensive OCR processing. A VM is recommended over an LXC container because OCR processing benefits from dedicated CPU allocation and predictable memory limits. Proxmox’s backup tooling with pre-freeze hooks gives you consistent snapshots of the database without manual intervention.
Prerequisites
- Proxmox VE 8.x installed and accessible
- An ISO uploaded to Proxmox storage (Ubuntu 22.04 Server or Debian 12 recommended)
- At least 4 GB RAM and 4 vCPUs to allocate
- 20 GB disk for the VM OS and application
- NAS or network storage for document files (optional but recommended)
Create the VM
Via Proxmox Web UI
- Click Create VM
- Configure:
- Name:
paperless - ISO: Ubuntu 22.04 Server
- Disk: 20 GB (on SSD storage if available — important for OCR I/O)
- CPU: 4 cores (2 minimum, but OCR benefits from more)
- Memory: 4096 MB (2 GB per OCR worker + 1 GB for OS and services)
- Network: virtio on your bridge
- Name:
Via CLI
qm create 120 --name paperless --cores 4 --memory 4096 \
--scsihw virtio-scsi-single --scsi0 local-lvm:20 \
--cdrom local:iso/ubuntu-22.04.4-live-server-amd64.iso \
--net0 virtio,bridge=vmbr0 --boot order=scsi0 --ostype l26
Start the VM and complete the Ubuntu installation through the VNC console.
NFS Mount for Document Storage
Storing documents on a NAS via NFS separates data from the VM. If the VM is destroyed or rebuilt, documents remain on the NAS. This also simplifies backup — the NAS handles document backups independently.
Inside the VM:
sudo apt install -y nfs-common
sudo mkdir -p /mnt/documents
# Mount NFS share from your NAS
sudo mount -t nfs nas-ip:/volume1/paperless /mnt/documents
# Auto-mount on boot
echo "nas-ip:/volume1/paperless /mnt/documents nfs defaults,_netdev 0 0" | sudo tee -a /etc/fstab
Create the required subdirectories:
sudo mkdir -p /mnt/documents/{media,consume,export}
sudo chown -R 1000:1000 /mnt/documents
If you do not have a NAS, use local VM storage. The Docker Compose below works with either approach — just change the volume paths.
Install Docker
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER
Log out and back in.
Docker Compose Configuration
mkdir -p /opt/paperless && cd /opt/paperless
Create docker-compose.yml:
services:
paperless:
image: ghcr.io/paperless-ngx/paperless-ngx:2.20.9
container_name: paperless
restart: unless-stopped
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
ports:
- "8000:8000"
environment:
PAPERLESS_DBHOST: db
PAPERLESS_DBPORT: 5432
PAPERLESS_DBNAME: paperless
PAPERLESS_DBUSER: paperless
PAPERLESS_DBPASS: secure_db_password # Change this
PAPERLESS_REDIS: redis://redis:6379
PAPERLESS_SECRET_KEY: generate-with-openssl-rand-hex-32 # Change this
PAPERLESS_URL: https://docs.yourdomain.com # Change to your access URL
PAPERLESS_ADMIN_USER: admin
PAPERLESS_ADMIN_PASSWORD: change_this_password # Change after first login
PAPERLESS_OCR_LANGUAGE: eng
PAPERLESS_TIME_ZONE: America/New_York # Your timezone
PAPERLESS_TASK_WORKERS: 2 # Match to vCPU count / 2
PAPERLESS_THREADS_PER_WORKER: 2 # 2 threads per worker
PAPERLESS_CONSUMER_POLLING: 30
PAPERLESS_CONSUMER_RECURSIVE: "true"
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS: "true"
USERMAP_UID: 1000
USERMAP_GID: 1000
volumes:
- paperless_data:/usr/src/paperless/data
- /mnt/documents/media:/usr/src/paperless/media # NFS or local
- /mnt/documents/consume:/usr/src/paperless/consume # Scanner drop folder
- /mnt/documents/export:/usr/src/paperless/export # Backup exports
networks:
- paperless-net
db:
image: postgres:16-alpine
container_name: paperless-db
restart: unless-stopped
environment:
POSTGRES_USER: paperless
POSTGRES_PASSWORD: secure_db_password # Must match PAPERLESS_DBPASS
POSTGRES_DB: paperless
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "paperless"]
interval: 10s
timeout: 5s
retries: 5
networks:
- paperless-net
redis:
image: redis:7-alpine
container_name: paperless-redis
restart: unless-stopped
volumes:
- redis_data:/data
networks:
- paperless-net
volumes:
paperless_data:
postgres_data:
redis_data:
networks:
paperless-net:
Start the stack:
docker compose up -d
Monitor startup:
docker compose logs -f paperless
Wait for Listening on 0.0.0.0:8000.
First-Time Setup
Open http://<vm-ip>:8000 and log in with the admin credentials from the environment variables. Change the password immediately.
Upload a test document to verify OCR processing works. With 4 vCPUs and 2 workers, expect 2-5 seconds per page.
Consume Folder via SMB or NFS
SMB Share (for Windows/Mac Scanners)
Inside the VM, set up Samba to expose the consume folder:
sudo apt install -y samba
sudo tee -a /etc/samba/smb.conf > /dev/null <<EOF
[paperless-scan]
path = /mnt/documents/consume
browseable = yes
writable = yes
valid users = paperless-user
create mask = 0644
directory mask = 0755
EOF
sudo useradd -M -s /sbin/nologin paperless-user
sudo smbpasswd -a paperless-user
sudo systemctl restart smbd
Direct NFS Consume
If your scanner can write to NFS (or you have another system pushing files), the NFS-mounted consume folder at /mnt/documents/consume is already accessible from the NAS side. Drop files there from any device with NFS access to your NAS.
Proxmox Backup with Pre-Freeze Script
The challenge with backing up Paperless is PostgreSQL consistency. A snapshot taken mid-transaction can result in a corrupted database. Proxmox supports pre-freeze and post-thaw hooks that run inside the VM during backup.
Set Up the QEMU Guest Agent
Inside the VM:
sudo apt install -y qemu-guest-agent
sudo systemctl enable --now qemu-guest-agent
In Proxmox, enable the QEMU Agent for the VM:
qm set 120 --agent enabled=1
Create the Pre-Freeze Script
Inside the VM, create /etc/paperless-backup-hooks.sh:
#!/bin/bash
# /etc/paperless-backup-hooks.sh
# Called by QEMU guest agent before Proxmox snapshot
ACTION=$1
case "$ACTION" in
freeze)
echo "$(date): Pre-freeze -- dumping PostgreSQL" >> /var/log/paperless-backup.log
docker exec paperless-db pg_dump -U paperless paperless > /opt/paperless/pre-snapshot-dump.sql
echo "$(date): Pre-freeze complete" >> /var/log/paperless-backup.log
;;
thaw)
echo "$(date): Post-thaw -- snapshot complete" >> /var/log/paperless-backup.log
;;
esac
chmod +x /etc/paperless-backup-hooks.sh
Configure the QEMU guest agent to use these hooks. Create /etc/qemu/fsfreeze-hook.d/paperless.sh:
sudo mkdir -p /etc/qemu/fsfreeze-hook.d
sudo cp /etc/paperless-backup-hooks.sh /etc/qemu/fsfreeze-hook.d/paperless.sh
sudo chmod +x /etc/qemu/fsfreeze-hook.d/paperless.sh
Now Proxmox snapshots automatically dump the database before freezing the filesystem.
Schedule Backups in Proxmox
In the Proxmox web UI:
- Go to Datacenter > Backup
- Click Add
- Set:
- Node: your node
- Storage: your backup storage
- Selection mode: Include
- VMs: 120 (paperless)
- Schedule: daily at 02:00
- Compression: ZSTD
- Mode: Snapshot
The pre-freeze hook runs automatically, giving you a consistent database dump inside every snapshot.
Resource Monitoring for OCR
OCR processing causes CPU spikes that can affect other VMs on the same host. Monitor from the Proxmox web UI under VM 120 > Summary.
Expected Resource Usage
| State | CPU | RAM | Disk I/O |
|---|---|---|---|
| Idle (no processing) | <5% | ~800 MB | Minimal |
| Single document OCR | 25-50% (2 workers) | 1.5-2.5 GB | Moderate reads/writes |
| Bulk import (queue of 100+ docs) | 100% sustained | 2.5-3.5 GB | Heavy |
Preventing OCR from Starving Other VMs
If OCR spikes interfere with other workloads, set CPU limits on the VM:
# Limit to 200% of one core (2 full cores out of 4 allocated)
qm set 120 --cpulimit 2
Or reduce OCR workers in Docker Compose:
PAPERLESS_TASK_WORKERS: 1
Alerts
Set up Proxmox email alerts for high resource usage. Under Datacenter > Options, configure SMTP settings. Proxmox automatically alerts on sustained high CPU or memory pressure.
Scaling Paperless on Proxmox
If your document volume grows:
- More OCR throughput: Increase vCPUs and
PAPERLESS_TASK_WORKERSproportionally. 1 worker per 2 vCPUs is a good ratio. - More storage: Expand the NFS share on your NAS, or add a disk to the VM.
- More RAM: Each OCR worker uses 500-800 MB during processing. Add 1 GB per additional worker.
To resize the VM without rebuilding:
# Stop VM first
qm set 120 --memory 8192
qm set 120 --cores 8
# Start VM
Troubleshooting
NFS Mount Fails on Boot
Symptom: Docker Compose fails because /mnt/documents is empty — NFS mounted after Docker started.
Fix: Add _netdev and x-systemd.automount to the fstab entry to ensure NFS mounts before Docker:
nas-ip:/volume1/paperless /mnt/documents nfs defaults,_netdev,x-systemd.automount 0 0
Also add depends_on or a systemd override for Docker to wait for the mount:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/wait-for-nfs.conf > /dev/null <<EOF
[Unit]
After=remote-fs.target
Requires=remote-fs.target
EOF
sudo systemctl daemon-reload
QEMU Guest Agent Not Responding
Symptom: Proxmox shows “QEMU Guest Agent not running” and pre-freeze hooks do not execute.
Fix: Inside the VM:
sudo systemctl status qemu-guest-agent
sudo systemctl enable --now qemu-guest-agent
If the service fails, check the VM hardware config in Proxmox includes the agent serial port (it is added automatically for new VMs but may be missing on older ones).
High CPU Usage Affecting Other VMs
Symptom: Other VMs on the same Proxmox host become sluggish when Paperless is processing documents.
Fix: Apply a CPU limit:
qm set 120 --cpulimit 2
This caps Paperless to 2 cores regardless of how many are allocated. Alternatively, reduce PAPERLESS_TASK_WORKERS to 1.
Database Corruption After Ungraceful Shutdown
Symptom: Paperless fails to start with PostgreSQL errors after a host crash or power loss.
Fix: PostgreSQL’s WAL should recover automatically. If it does not, restore from the pre-freeze dump:
docker compose down
docker volume rm paperless_postgres_data
docker compose up -d db
# Wait for db to initialize
docker exec -i paperless-db psql -U paperless paperless < /opt/paperless/pre-snapshot-dump.sql
docker compose up -d
If no dump exists, restore from the latest Proxmox backup.
Consume Folder Permissions With NFS
Symptom: Files in the consume folder are not processed. Logs show permission errors.
Fix: NFS UID/GID mapping must match the container’s USERMAP_UID/USERMAP_GID (1000 by default). On the NAS, ensure the NFS export allows write access from the VM’s IP and that files are owned by UID 1000:
# On the NAS or NFS server
chown -R 1000:1000 /volume1/paperless/consume
Some NAS systems (Synology, TrueNAS) have “squash” settings that remap UIDs — set squash to no_root_squash or map to the correct UID.
Resource Requirements
- VM allocation: 4 vCPU, 4 GB RAM, 20 GB disk (plus NFS for documents)
- Actual usage: ~800 MB idle, 2.5-3.5 GB during OCR with 2 workers
- Storage: Plan 1-5 MB per document page (original + OCR archive)
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments