Skip to content
appsgit

Deploy guide

How to self-host Paperless-ngx with Docker Compose

Set up Paperless-ngx with Docker Compose, Postgres and Redis: secret key, OCR languages, consume folder, HTTPS, backups with the exporter and upgrades.

  • Updated
  • Beginner
  • About 20 minutes

You will need

  • 2 vCPU / 2 GB RAM (4 GB recommended for OCR)
  • Docker + Docker Compose v2
  • Disk space for scanned documents
  • A domain name (optional, for HTTPS)

What is Paperless-ngx?

Paperless-ngx is a document management system that scans, OCRs, indexes and archives your paper documents so you can search them like email. It is open source under the GPL-3.0 license and is the community-maintained successor to Paperless and Paperless-ng. It learns how to tag, file and date incoming documents automatically, and stores everything as searchable PDF/A.

Requirements

  • A Linux server with 2 vCPU and 2 GB of RAM. OCR is CPU-heavy, so 4 GB and more cores speed up large imports.
  • Docker Engine and Docker Compose v2.
  • Disk space for originals plus archived PDF/A copies.

Step 1: Prepare the server

This guide assumes Ubuntu 24.04 with Docker installed. If you need Docker, see the official install guide. Create a project folder with the directories Paperless uses for import and export:

mkdir -p ~/paperless/{consume,export} && cd ~/paperless
id -u && id -g

Note your user ID and group ID. Paperless will run with them so you can drop files into consume without permission problems.

Step 2: Create the Docker Compose file

The official stack uses Postgres as the database and Redis (or Valkey) as the task broker. Save this as docker-compose.yml:

services:
  broker:
    image: docker.io/valkey/valkey:8-alpine
    restart: unless-stopped
    volumes:
      - redisdata:/data

  db:
    image: docker.io/library/postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data

  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: unless-stopped
    depends_on:
      - db
      - broker
    ports:
      - "8000:8000"
    volumes:
      - data:/usr/src/paperless/data
      - media:/usr/src/paperless/media
      - ./export:/usr/src/paperless/export
      - ./consume:/usr/src/paperless/consume
    environment:
      PAPERLESS_REDIS: redis://broker:6379
      PAPERLESS_DBHOST: db
      PAPERLESS_DBENGINE: postgresql
      PAPERLESS_DBPASS: ${POSTGRES_PASSWORD}
      PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}
      PAPERLESS_URL: https://docs.example.com
      PAPERLESS_TIME_ZONE: Europe/London
      PAPERLESS_OCR_LANGUAGE: eng
      USERMAP_UID: 1000
      USERMAP_GID: 1000

volumes:
  data:
  media:
  pgdata:
  redisdata:

Create .env in the same folder:

POSTGRES_PASSWORD=CHANGE_ME
PAPERLESS_SECRET_KEY=CHANGE_ME

Generate each value with openssl rand -hex 32. Set USERMAP_UID and USERMAP_GID to the numbers from Step 1, and PAPERLESS_URL to the exact public URL you will use. For OCR in several languages, join codes with a plus sign, for example deu+eng. To pin a release instead of latest, use a version tag from the releases page.

Step 3: Start and open the app

docker compose up -d
docker compose run --rm webserver createsuperuser

The second command prompts for an admin username, email and password. Then open http://YOUR_SERVER_IP:8000 and sign in. To test the pipeline, copy a PDF into ~/paperless/consume: within a minute Paperless picks it up, runs OCR and shows it in the document list. You can also upload through the web UI or set up mail rules to fetch attachments from an inbox.

Step 4: Put it behind HTTPS

With Caddy on the host:

docs.example.com {
    request_body {
        max_size 200MB
    }
    reverse_proxy 127.0.0.1:8000
}

Make sure PAPERLESS_URL matches this domain exactly, including https://. The UI uses WebSockets for live upload status, which Caddy passes automatically. With Nginx Proxy Manager, enable "Websockets Support". Once HTTPS works, change the port mapping to "127.0.0.1:8000:8000".

Backups and upgrades

The most reliable backup is the built-in document exporter, which writes originals, archived PDFs and all metadata to the export folder:

docker compose exec -T webserver document_exporter ../export

Copy ~/paperless/export off-site afterwards, encrypted. Also keep your .env. The exporter output can be re-imported into a fresh install with document_importer.

To upgrade, back up first, read the release notes, then run:

docker compose pull && docker compose up -d

Database migrations run automatically on start.

Troubleshooting

  • "CSRF verification failed" when logging in: PAPERLESS_URL does not match the address in your browser. Fix it and recreate the container.
  • Files in consume are ignored: check permissions (the folder must be writable by USERMAP_UID). On network shares, set PAPERLESS_CONSUMER_POLLING=30, because inotify events do not cross NFS or SMB.
  • OCR fails with a language error: the language code is not installed. Add it to PAPERLESS_OCR_LANGUAGES so the container installs it on start.
  • Imports are slow: add CPU cores, or lower OCR work with PAPERLESS_OCR_MODE=skip for documents that already contain text.

Next steps

Create correspondents, document types and tags, train the automatic matching, add a third-party mobile app for scanning on the go, and schedule the exporter with cron.

Spotted something out of date? Tell us and we will update the guide.

FAQ

Paperless-ngx questions

Still curious? Email info@appsgit.com.

What port does Paperless-ngx use?

The Paperless-ngx web server listens on port 8000. You open it at http://SERVER_IP:8000, or through a reverse proxy on your own domain.

Is Paperless-ngx free?

Yes. Paperless-ngx is free and open source under the GPL-3.0 license and is maintained by a community team.

What is PAPERLESS_SECRET_KEY?

It is the key Django uses to sign session cookies and tokens. Set a long random value, keep it stable, and never share it, because anyone who knows it can forge sessions.

How do I add OCR languages to Paperless-ngx?

Set PAPERLESS_OCR_LANGUAGE to the Tesseract codes your documents use, for example deu+eng. Languages not bundled in the image can be installed at start with PAPERLESS_OCR_LANGUAGES.

Why do I need PAPERLESS_URL?

Paperless-ngx uses it for CSRF protection and allowed hosts. Without it, logins through a reverse proxy on your domain fail with a CSRF verification error.

Paperless-ngx vs Docspell?

Both are self-hosted document management systems with OCR. Paperless-ngx has the larger community, a polished web UI and several third-party mobile apps, while Docspell focuses on multi-user collectives and flexible custom metadata.