We earn commissions when you shop through the links below.
Full disclosure → A self-hosted document management system that OCRs, tags, and full-text-indexes your scanned paperwork into a searchable, paperless archive.
Our recommendationPick Paperless-ngx when you want scanned paper turned into a searchable archive on hardware you control and you accept the project's own warning that it "should never be run on an untrusted host" — documents are stored in clear text, and upstream's advice is a local server at home with backups. It is GPL-3.0, Python/Django, and the compose stack is three containers: `ghcr.io/paperless-ngx/paperless-ngx`, a required Valkey (Redis-protocol) broker, and PostgreSQL (recommended) or MariaDB; SQLite is the default engine and the one upstream suggests for a Raspberry Pi. OCR runs through OCRmyPDF, and upstream's low-power guidance is about worker counts and trimming memory use — it publishes no RAM figure.
Follow the Paperless-ngx deploy guide → What you take on
Paperless-ngx's docs are good, but v3 changed several defaults and the OCR pipeline needs tuning on small boxes:
non-negotiablePAPERLESS_SECRET_KEY is required since v3 — "Paperless will refuse to start if this is not set" — and installs that relied on the old built-in default must set it to that value or invalidate every session and token. The install script generates one; hand-written compose files must add it (python3 -c "import secrets; print(secrets.token_urlsafe(64))"). v3 also requires PAPERLESS_DBENGINE=postgresql (or mariadb) explicitly, can only be upgraded to from 2.20.15, and comes with a migration guide upstream says to read first.
non-negotiableBehind a reverse proxy, or on any real hostname, set PAPERLESS_URL (no trailing slash, no path): it fills ALLOWED_HOSTS, CORS_ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS, and the CSRF list is what lets POSTs through. Left alone, PAPERLESS_ALLOWED_HOSTS defaults to *, which upstream says leaves an internet-facing install open to host-header attacks.
non-negotiableThe consume, media and data folders are chown'd to the container user at start; set USERMAP_UID and USERMAP_GID to your host user if it is not 1000, or the consumer fails with Permission denied when it tries to delete a processed file. NFS shares that refuse chown fail with Operation not permitted, and filesystems without inotify need PAPERLESS_CONSUMER_POLLING_INTERVAL set to a positive number.
non-negotiableBack up with docker compose exec -T webserver document_exporter ../export — it writes documents, thumbnails, metadata and database contents to a folder and supports incremental rsync — but an export can only be imported into the same Paperless version, and API tokens are not included. Updating is docker compose down, docker compose pull, docker compose up, which also applies migrations; take the export first.
non-negotiableThe 2 GB floor is our estimate; upstream publishes no RAM requirement. Its low-power guidance is to stay on SQLite, keep PAPERLESS_TASK_WORKERS * PAPERLESS_THREADS_PER_WORKER at or below your core count, set PAPERLESS_WEBSERVER_WORKERS=1 ("This will save some memory") and PAPERLESS_OCR_CLEAN=none, and set PAPERLESS_CONVERT_MEMORY_LIMIT (e.g. 32) if the consumer dies with "unable to extend pixel cache".