Fully verified44,757

Documents

Paperless-ngx

Paperless-ngx ingests scans and PDFs, runs OCR over them, extracts metadata, and turns a document archive into a searchable full-text database. This recipe uses PostgreSQL and Valkey, keeps originals locally, and exposes the web interface on localhost only.

Overview

Paperless-ngx turns a folder of scans and PDFs into a searchable archive. It keeps originals, creates PDF/A renditions and thumbnails, recognizes text, extracts metadata, and automatically suggests tags, document types, and correspondents. It suits personal, family, and small workplace archives and can replace cloud document storage and manual folder searches.

This recipe runs the pinned Paperless-ngx 3.1.2 image with PostgreSQL 18 and Valkey 9. English OCR is enabled by default, and one variable adds more languages. Application data, originals and derived files, import/export, the database, and broker state use separate Docker volumes. Tika and Gotenberg are omitted, so this recipe does not support Office and email ingestion that requires those services.

Security and recipe boundaries

The full HTTP smoke test has passed on amd64; document export and restore remain untested in practice. Paperless-ngx does not encrypt documents or recognized text at rest; sensitive archives need disk encryption and encrypted off-server backups. OCR, Ghostscript, and image handlers process untrusted files that can exhaust resources or attack parsers. The web port binds to localhost, while PostgreSQL and Valkey are not published at all.

Update tracking

These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.

New releases

Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.

Current version 3.1.2Sep 2, 2026, 9:26 AM UTC

Critical vulnerabilities

Result of a dedicated Docker image scan, not a Compose configuration check.

Not checkedNot checked

Breaking updates

A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.

Not checkedNot checked

compose.yaml

Upstream Compose changes relative to the verified recipe.

Upstream Compose is not trackedNot checked

Outdated Docker images

Pinned image tags checked for newer versions.

Not checkedNot checked

From observation to action

Update plan

The recipe already pins the latest known release. The steps below show the evidence retained for that version.

3.1.23.1.2
  1. Open the release evidence

    Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.

  2. Review the version boundary

    The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.

  3. Prepare image-tag changes

    No image-tag change is currently planned.

  4. Follow the documented migration shape

    Manual steps after the pull. Release-specific migration steps still come from the release notes and installation guide.

  5. Protect the rollback point

    No version change is currently planned. The recipe includes a backup procedure. Restore has not been tested.

  6. Verify the updated recipe

    An exact Fully verified check for this target was recorded on 2026-09-03.

Read the update procedure in the installation guide ↓

Specifications

Security and exposure

Installation levels

The status shows how fully the published recipe meets each level's requirements.

Partial

For home

A minimal setup for a local network or access through a VPN.

  • Single server
  • Local access
  • Minimum components

You still need to add: single server, local access, minimum components.

Partial

For a VPS

A public deployment with protected access and backups.

  • Domain
  • HTTPS via reverse proxy
  • Off-server backup

You still need to add: domain, https via reverse proxy, off-server backup.

Partial

Reliable

A setup designed for predictable operations and observability.

  • Separate database
  • Healthcheck
  • External monitoring
  • CPU and memory limits

You still need to add: external monitoring, cpu and memory limits.

Tailored to your server

Configuration generator

Answer five questions and download a ready-to-run Paperless-ngx bundle.

Runs locally
01 Do you have a domain?
02 Which reverse proxy?
03 Server architecture
04 Where should data be stored?

Use an absolute path for a separate disk, such as /srv/paperless-ngx.

05 Is VPN access required?

Your bundle is ready

The archive is created in your browser. Your answers are never sent anywhere.

Screenshots

Screenshots have not been added yet.

Quick start

Read the full guide first: it explains external access, backups, and data locations.

Prepare settingscp .env.example .env
Launchdocker compose up -d
Check statusdocker compose ps

Step-by-step guide

1. Check the Ubuntu or Debian server

Use Ubuntu 22.04+ or Debian 12+ with Docker Engine and Compose v2.24+. Allocate at least 2 CPUs, 2 GB RAM, and 10 GB disk; 4 GB RAM is recommended. OCR briefly loads the CPU and needs extra room for originals, PDF/A renditions, and thumbnails.

docker --version
docker compose version

2. Prepare the recipe and secrets

mkdir -p ~/services/paperless-ngx
cd ~/services/paperless-ngx
cp .env.example .env
chmod 600 .env
sed -i "s|^PAPERLESS_SECRET_KEY=.*|PAPERLESS_SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_urlsafe(64))')|" .env
sed -i "s|^PAPERLESS_DB_PASSWORD=.*|PAPERLESS_DB_PASSWORD=$(openssl rand -hex 32)|" .env

Never publish .env. PAPERLESS_SECRET_KEY signs sessions and sensitive data; PAPERLESS_DB_PASSWORD protects PostgreSQL. Both are required. Store them in a secret manager and do not change them when restoring an existing archive.

Every .env variable:

  • PAPERLESS_PORT is the local web port, default 8000;
  • PAPERLESS_URL is the sole public origin with https, no trailing /, and no path; it configures allowed hosts, CORS, and CSRF origins;
  • PAPERLESS_SECRET_KEY is the required random signing key;
  • PAPERLESS_DB_PASSWORD is the required random database password;
  • PAPERLESS_DB_NAME and PAPERLESS_DB_USER name the database and role and should change only before first start;
  • PAPERLESS_TIME_ZONE is an IANA time zone for dates and background tasks;
  • PAPERLESS_OCR_LANGUAGE is what to recognize in a document, default eng; use rus+eng for two;
  • PAPERLESS_OCR_LANGUAGES is which tesseract packs to install at start, so add rus here before naming it above;
  • the PAPERLESS_*_VOLUME variables name data, media, export, consume, PostgreSQL, and Valkey volumes;
  • PAPERLESS_BACKUP_DIR selects the final archive directory on the host.

paperless-data holds the index, classifier, and auxiliary state; paperless-media stores originals, archive renditions, and thumbnails; paperless-export stores exporter output; paperless-consume holds incoming files; the remaining volumes store the PostgreSQL cluster and Valkey state. The recipe encrypts none of them.

3. Start and create an administrator

docker compose config
docker compose pull
docker compose up -d --wait
curl -I --fail http://127.0.0.1:8000/
docker compose exec webserver createsuperuser

The last command asks for username, email, and password interactively. The password enters neither .env nor shell history and does not remain in the container environment. Self-registration is disabled. Do not expose the service before creating the administrator. Inspect it with docker compose ps and docker compose logs --tail=100 webserver.

VPS deployment

Keep 127.0.0.1:${PAPERLESS_PORT}:8000; only an HTTPS reverse proxy on the host should reach web. PostgreSQL and Valkey have no published ports. Allow only SSH, HTTP, and HTTPS through the firewall. Before the first login, replace the domain in PAPERLESS_URL and the proxy example, then configure DNS and TLS.

Trusted LAN access

Without a domain, keep the localhost binding and use ssh -L 8000:127.0.0.1:8000 user@server; set PAPERLESS_URL=http://localhost:8000 and recreate webserver for that temporary access. For permanent LAN access, replace 127.0.0.1 in compose.yaml with a specific private address such as 192.168.1.10, set PAPERLESS_URL=http://192.168.1.10:8000, and restrict the port by firewall. Do not use 0.0.0.0 without network controls.

Domain, HTTPS, and WebSockets

Set PAPERLESS_URL=https://paperless.example.com with no trailing slash. A path such as /paperless is invalid in this setting. Replace the domain in proxy/Caddyfile, proxy/nginx.conf, or proxy/traefik.yaml. Caddy obtains a certificate, Nginx expects Certbot files, and Traefik uses the letsencrypt resolver. Then run:

docker compose up -d --force-recreate webserver

The proxy must preserve Host, send X-Forwarded-Proto: https, and forward the client address. Background processing status uses /ws/status/ WebSockets: Caddy and Traefik handle upgrades automatically, while Nginx is configured explicitly. For a containerized proxy, 127.0.0.1 refers to that proxy; use a reachable host gateway instead.

Backup

Wait for tasks to finish and ensure consume is empty, then run:

chmod +x backup.sh restore.sh
./backup.sh

The script runs the official document_exporter and packages its export, unconsumed files, .env, and Compose into one tar. The export contains documents, thumbnails, metadata, users, and an exact data snapshot, but does not contain API tokens; issue new tokens after restore. Changes started during export may not belong to one consistent snapshot, so do not ingest documents or edit metadata during the operation. The archive contains documents and secrets: encrypt it, keep an off-server copy, and test restores.

Restore

Import irreversibly replaces all six volumes. It is supported only into a completely empty installation of the same Paperless-ngx version, with the same path settings. Verify tag 3.1.2, free space, and the active .env, then:

./restore.sh ./backups/paperless-ngx-YYYYMMDDTHHMMSSZ.tar
docker compose ps
docker compose exec webserver document_sanity_checker

The script first exports the state about to be replaced, removes the volumes, starts an empty installation, and runs document_importer. The archived configuration.env remains a reference for manual comparison and does not replace active .env. This procedure has not passed a practical restore test; test a copy on another server first. Issue new API tokens after import.

Update Paperless-ngx

Wait for tasks, back up, and read the release notes and migration instructions. Replace only the exact paperlessngx/paperless-ngx:3.1.2 tag with a reviewed version; never use latest. Then run:

docker compose pull
docker compose up -d --wait
docker compose logs --tail=200 webserver
docker compose exec webserver document_sanity_checker

Startup applies migrations automatically. Do not update Paperless, PostgreSQL, and Valkey together; separate changes keep failures and rollback unambiguous.

PostgreSQL major update

postgres:18-alpine is pinned independently. Changing major is not a normal docker compose pull: data directory formats can be incompatible. Follow the official PostgreSQL pg_upgrade or dump/restore procedure, or use document_exporter --data-only and importer with a new empty database. Make and verify a full backup first; do not change paths or remove the old volume before verifying the new database.

Rollback

Never run an older image over a database after migrations. Restore the previous exact tag and the complete pre-update export through restore.sh; this rolls back database, documents, and index together. For a failed PostgreSQL major update, restore the old tag and old volume. Never attach an old server to a new major’s data directory.

Stop and remove

docker compose down preserves data. After verifying an external backup, remove everything irreversibly:

docker compose down
docker volume rm paperless-data paperless-media paperless-export paperless-consume paperless-database paperless-broker
rm -rf ~/services/paperless-ngx

Substitute the actual names when volume variables differ in .env.

Sources: configuration, backup, exporter/importer, and update, release 3.1.2, and PostgreSQL major upgrades.

Troubleshooting

Paperless-ngx does not become healthy

docker compose ps
docker compose logs --tail=200 webserver database broker
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q webserver)"

Verify that both required secrets were replaced, PostgreSQL and Valkey are healthy, disk space is available, and curl http://127.0.0.1:8000/ returns a redirect. The first start takes longer while migrations, indexing, and OCR language installation run.

PAPERLESS_URL must exactly match the public origin: https scheme, domain, optional nonstandard port, no trailing /, and no path. After changing it, run docker compose up -d --force-recreate webserver. Verify that the proxy keeps the original Host and sends X-Forwarded-Proto.

Processing status does not update

Inspect the WebSocket route and broker:

docker compose logs --tail=200 webserver broker
docker compose exec broker valkey-cli ping

The proxy must permit connection upgrades for /ws/status/. A containerized proxy cannot reach host 127.0.0.1 without a host gateway.

OCR does not recognize a language

Two variables have to agree. PAPERLESS_OCR_LANGUAGES installs the tesseract pack at container start, and PAPERLESS_OCR_LANGUAGE selects what to recognize; naming a language only in the second one silently does nothing. For Russian alongside English set PAPERLESS_OCR_LANGUAGES=rus and PAPERLESS_OCR_LANGUAGE=rus+eng, then check the startup logs for the language data installation. Re-run OCR on existing documents through the UI only after a backup: it is CPU intensive and may replace the archived rendition.

A document remains in consume or processing fails

docker compose logs --since=30m webserver
docker system df
docker compose exec webserver document_sanity_checker

Check the format, volume permissions, free disk, and RAM. Tika and Gotenberg are not included, so this stack cannot process Office and email files that require them.

The reverse proxy returns 502

On the host, run curl -I http://127.0.0.1:8000/. Then inspect the upstream, firewall, and proxy network namespace. Inside a container, 127.0.0.1 refers to that container, not Paperless-ngx on the host.

Official sources

Latest release: 2026-09-01 · GitHub Stars: 44,757 · metadata checked Sep 1, 2026, 12:21 AM UTC

Disclaimer

Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.

A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.