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:

  1. Slow OCR processing significantly (random I/O is 10-50x slower than SSD)
  2. Wear out within months under sustained write load
  3. 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:

HardwarePer Page (PDF)Per Page (Scanned Image)
Pi 4 (4 GB)20-40 seconds30-60 seconds
Pi 5 (4 GB)10-20 seconds15-30 seconds
x86 (i5)2-5 seconds3-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

Comments