Install Paperless-ngx on Raspberry Pi
Why Paperless-ngx on a Pi
A Raspberry Pi 4 (4 GB) or Pi 5 can run Paperless-ngx for a household’s document management needs. The ARM64 images work natively, and once documents are OCR-processed and indexed, day-to-day browsing and searching is snappy. The trade-off: OCR processing is slow. A page that takes 3 seconds on an Intel i5 takes 20-40 seconds on a Pi 4. If you scan a few documents a day, that is fine. If you are bulk-importing thousands of pages, use a more powerful machine.
Prerequisites
- Raspberry Pi 4 (4 GB minimum) or Pi 5 running 64-bit Raspberry Pi OS (Bookworm)
- Docker and Docker Compose installed (guide)
- USB SSD (required — PostgreSQL and OCR processing will kill an SD card)
- 20 GB free disk space on the SSD
- Ethernet connection recommended
Why USB SSD Is Required
This is not a suggestion. Paperless-ngx with PostgreSQL and Redis writes heavily to disk during OCR processing and database operations. An SD card will:
- Slow OCR processing significantly (random I/O is 10-50x slower than SSD)
- Wear out within months under sustained write load
- Corrupt PostgreSQL data if it fails mid-write
Set up a USB SSD before proceeding:
# Identify the SSD
lsblk
# Format (assuming /dev/sda1)
sudo mkfs.ext4 /dev/sda1
sudo mkdir -p /mnt/ssd
sudo mount /dev/sda1 /mnt/ssd
# Auto-mount on boot
echo "/dev/sda1 /mnt/ssd ext4 defaults,noatime 0 2" | sudo tee -a /etc/fstab
# Create directories for Paperless
mkdir -p /mnt/ssd/paperless/{data,media,postgres,redis,consume,export}
Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
Log out and back in, then verify:
docker run --rm hello-world
Increase Swap
OCR processing can spike memory usage above 4 GB. Increase swap to prevent OOM kills:
sudo dphys-swapfile swapoff
sudo sed -i 's/CONF_SWAPSIZE=.*/CONF_SWAPSIZE=2048/' /etc/dphys-swapfile
sudo dphys-swapfile setup
sudo dphys-swapfile swapon
Verify:
free -h
# Swap should show ~2 GB
With the swap on the USB SSD rather than the SD card, this is safe for sustained use.
Docker Compose Configuration
Create the project directory:
mkdir -p /mnt/ssd/paperless/config && cd /mnt/ssd/paperless/config
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: change_this_password # Change this
PAPERLESS_REDIS: redis://redis:6379
PAPERLESS_SECRET_KEY: generate-a-long-random-string # Generate with: openssl rand -hex 32
PAPERLESS_URL: http://paperless.local:8000 # Your Pi's hostname or IP
PAPERLESS_ADMIN_USER: admin
PAPERLESS_ADMIN_PASSWORD: admin # Change this after first login
PAPERLESS_OCR_LANGUAGE: eng
PAPERLESS_TIME_ZONE: America/New_York # Your timezone
# Pi-specific tuning -- reduce resource usage
PAPERLESS_TASK_WORKERS: 1 # Single OCR worker to avoid OOM
PAPERLESS_THREADS_PER_WORKER: 1 # Single thread per worker
PAPERLESS_CONSUMER_POLLING: 60 # Check consume folder every 60 seconds (less frequent = less CPU)
PAPERLESS_CONSUMER_RECURSIVE: "true"
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS: "true"
PAPERLESS_OCR_PAGES: 0 # OCR all pages (set to a limit if processing is too slow)
PAPERLESS_WEBSERVER_WORKERS: 1 # Single web worker to save RAM
USERMAP_UID: 1000
USERMAP_GID: 1000
volumes:
- /mnt/ssd/paperless/data:/usr/src/paperless/data
- /mnt/ssd/paperless/media:/usr/src/paperless/media
- /mnt/ssd/paperless/consume:/usr/src/paperless/consume
- /mnt/ssd/paperless/export:/usr/src/paperless/export
networks:
- paperless-net
db:
image: postgres:16-alpine
container_name: paperless-db
restart: unless-stopped
environment:
POSTGRES_USER: paperless
POSTGRES_PASSWORD: change_this_password # Must match PAPERLESS_DBPASS
POSTGRES_DB: paperless
volumes:
- /mnt/ssd/paperless/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "paperless"]
interval: 10s
timeout: 5s
retries: 5
# Tune PostgreSQL for Pi's limited RAM
command: >
postgres
-c shared_buffers=128MB
-c effective_cache_size=256MB
-c work_mem=4MB
-c maintenance_work_mem=64MB
-c max_connections=20
networks:
- paperless-net
redis:
image: redis:7-alpine
container_name: paperless-redis
restart: unless-stopped
volumes:
- /mnt/ssd/paperless/redis:/data
# Limit Redis memory on Pi
command: redis-server --maxmemory 64mb --maxmemory-policy allkeys-lru
networks:
- paperless-net
volumes: {}
networks:
paperless-net:
Start the stack:
docker compose up -d
First startup takes 2-5 minutes on a Pi as the database initializes. Monitor progress:
docker compose logs -f paperless
Wait for Listening on 0.0.0.0:8000.
First-Time Setup
Open http://<pi-ip>:8000 and log in with admin / admin (or whatever you set in the environment variables). Change the admin password immediately under Profile > Change Password.
Upload a test document (PDF or image) by dragging it onto the dashboard. On a Pi 4, expect OCR processing to take 20-40 seconds per page. You can monitor processing in the Tasks section.
Scanner Integration
The simplest way to get documents from a scanner to the Pi is a Samba share:
sudo apt install -y samba
sudo tee -a /etc/samba/smb.conf > /dev/null <<EOF
[paperless]
path = /mnt/ssd/paperless/consume
browseable = yes
writable = yes
valid users = $USER
create mask = 0644
directory mask = 0755
EOF
sudo smbpasswd -a $USER
sudo systemctl restart smbd
Point your scanner to \\pi-ip\paperless. Scanned documents appear in the consume folder and are automatically processed.
Pi-Specific Optimization
Disable Features You Do Not Need
Every feature consumes RAM. Disable what you will not use:
# If you don't use document matching rules based on AI classification
PAPERLESS_DISABLE_REGULAR_CHECKS: "true"
# If you don't need email fetching
PAPERLESS_CONSUMER_ENABLE_BARCODES: "false"
OCR Performance Expectations
Realistic OCR speeds on Raspberry Pi hardware:
| Hardware | Per Page (PDF) | Per Page (Scanned Image) |
|---|---|---|
| Pi 4 (4 GB) | 20-40 seconds | 30-60 seconds |
| Pi 5 (4 GB) | 10-20 seconds | 15-30 seconds |
| x86 (i5) | 2-5 seconds | 3-8 seconds |
For bulk imports (hundreds of pages), start the import and let it run overnight. Do not expect real-time processing.
Limit OCR to Reduce Load
If you primarily work with already-digital PDFs (not scanned images), you can skip OCR on documents that already have text:
PAPERLESS_OCR_MODE: skip_noarchive
This only OCR-processes documents without embedded text, significantly reducing CPU load for digital-first workflows.
Temperature Management
OCR processing pins the CPU at 100% for extended periods. Monitor temperature:
vcgencmd measure_temp
Use an active cooling case or heatsink with fan. The Pi throttles at 80C, which extends OCR processing time even further. A good cooling solution cuts processing time by 15-25%.
Backup
#!/bin/bash
# backup-paperless-pi.sh
BACKUP_DIR="/mnt/ssd/backups/paperless/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"
# Use Paperless built-in exporter
docker exec paperless document_exporter /usr/src/paperless/export
# Copy export to backup location
cp -r /mnt/ssd/paperless/export "$BACKUP_DIR/"
# Database dump
docker exec paperless-db pg_dump -U paperless paperless > "$BACKUP_DIR/db.sql"
echo "Backup saved to $BACKUP_DIR"
Copy backups to a network share or external drive. Never keep your only backup on the same SSD. See Backup Strategy.
Troubleshooting
OOM Killer Terminates Paperless
Symptom: Paperless or PostgreSQL stops unexpectedly. dmesg | grep -i oom shows OOM kill.
Fix: You are running too many workers. Set PAPERLESS_TASK_WORKERS: 1 and PAPERLESS_WEBSERVER_WORKERS: 1. Verify swap is enabled and set to 2 GB. If still happening on a 4 GB Pi, check if other services are competing for RAM.
Container Fails to Start on 32-bit OS
Symptom: exec format error on container start.
Fix: Paperless-ngx ARM images require 64-bit. Check your OS:
uname -m
If it shows armv7l, you are on 32-bit. Reflash with 64-bit Raspberry Pi OS.
Consume Folder Files Not Processing
Symptom: Documents sit in /mnt/ssd/paperless/consume without being imported.
Fix: Check ownership matches USERMAP_UID/USERMAP_GID:
sudo chown -R 1000:1000 /mnt/ssd/paperless/consume
Check container logs for errors:
docker compose logs paperless | grep -i error
Also verify PAPERLESS_CONSUMER_POLLING is set — without it, the consumer may not start.
Extremely Slow Web UI
Symptom: Page loads take 10+ seconds, thumbnails do not load.
Fix: The search index may be too large for available RAM. Rebuild it:
docker exec paperless document_index reindex
If still slow, add PAPERLESS_ENABLE_COMPRESSION: "true" and consider reducing PAPERLESS_THUMBNAIL_FONT_NAME to a smaller font.
USB SSD Not Detected After Reboot
Symptom: Docker Compose fails because /mnt/ssd is not mounted.
Fix: Verify the fstab entry uses a stable device identifier. Using /dev/sda1 can change if USB devices are enumerated in different order. Use UUID instead:
sudo blkid /dev/sda1
# Note the UUID
# Edit /etc/fstab to use UUID:
# UUID=your-uuid-here /mnt/ssd ext4 defaults,noatime 0 2
Resource Requirements
- RAM: ~800 MB idle (Paperless + PostgreSQL + Redis). 1.5-2.5 GB during active OCR
- CPU: Low idle. 100% sustained during OCR processing (single core per worker)
- Disk: 1 GB for the application stack, plus document storage
- Minimum Pi: Pi 4 with 4 GB RAM. The 2 GB model will OOM under OCR load
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