Paperless-ngx with Docker: A Paperless Office with OCR and Local AI Tagging (Linux, Windows, Mac)
Before you start
- 19 min read
- 14 to run in terminal
- 4 file contents
Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#
Paperless-ngx is a free, self-hosted document archive: you drop in scans and files, it reads the text with OCR, and you find any page later by searching.
People use it to get rid of the paper drawer. Invoices, contracts, letters and receipts go in, and tags, correspondents and document types keep them tidy. Your documents stay on your own machine. This guide builds the official stack (PostgreSQL, a queue, Gotenberg and Tika for Office files) with pinned versions, then adds an optional helper that suggests titles and tags with a local AI model.
| What | Value |
|---|---|
| Paperless-ngx version | 3.3.4, released 2026-10-22 (GitHub releases) |
| Image | ghcr.io/paperless-ngx/paperless-ngx:3.3.4 |
| AI helper (optional) | paperless-gpt v0.31.0, released 2026-10-14 |
| Time needed | About 15-20 minutes, plus the first image pulls |
| Ports | 8000 Paperless, 8080 paperless-gpt (both on 127.0.0.1 only) |
| Official docs | https://docs.paperless-ngx.com/ |
Which version? The newest release, 3.3.4, is a security-maintenance release. We checked the issue tracker: the one startup-loop report was caused by a custom command: override in the reporter's own Compose file, and another report came from a third-party non-Docker script that the maintainers closed as not a bug. We found no repeated reports of a broken Docker install, so this guide uses the latest release. It is only four days old at the time of writing, so read the release notes before you update later.
If you already run Paperless-ngx 2.x, read the official v3 migration notes before you change the image tag.
How it fits together#
Files arrive by folder, mail or upload, Paperless-ngx reads them, and the optional AI helper only suggests metadata that you approve.
Two things worth knowing. First, the current official Compose file uses Valkey (a Redis-compatible queue) instead of a Redis-branded image. Paperless still calls the setting PAPERLESS_REDIS and the address redis://broker:6379. Second, paperless-gpt asks the model for suggestions based on the text Paperless already extracted. It does not replace the normal OCR here.
Which AI helper? paperless-gpt is the active one. The paperless-ai README now says plainly that the repository is not maintained, so we skip it.
Before you start#
You need Docker with the Compose plugin, a terminal, and a few GB of free disk space.
| System | What you need | Test status |
|---|---|---|
| Linux | Docker Engine with the docker compose plugin, bash | Checked against official docs, not run |
| Windows | Docker Desktop (Linux containers, WSL2 backend), PowerShell 7 | Checked against official docs, not run |
| Mac | Docker Desktop for Mac, Terminal | Checked against official docs, not run |
Check Docker first:
docker --version docker compose version
Paperless-ngx has no official minimum for this whole stack. As a starting point, plan for 2-4 CPU cores and about 4 GB of free RAM for the document stack (VERIFY on your own machine). A local AI model needs extra memory on top, and 16 GB in total is a sensible start for an 8B-class model (VERIFY, it depends on the model and context size). The one official tuning rule: workers times threads must not be more than your CPU cores.
- [ ] Ports 8000 and 8080 are free on the machine
- [ ] You have a place for backups on another disk or machine
- [ ] Windows only: Docker Desktop is running in Linux-container mode
Tested on: not run. This guide was checked against the official docs and compose files on 2026-10-26; the commands were not executed on Linux, Windows or Mac.
Step 1: Create the folders and secrets#
Make one folder under ~/docker_volumes/paperless-ngx and generate a database password and a secret key once.
Paperless needs PAPERLESS_SECRET_KEY (the official env file marks it required). The database password and the key go in a .env file that only you can read. Run this only for a new install. If you generate new values later, the database password no longer matches the existing database.
Linux#
set -eu umask 077 mkdir -p "$HOME/docker_volumes/paperless-ngx" cd "$HOME/docker_volumes/paperless-ngx" mkdir -p data media consume export postgres broker gpt/prompts gpt/config gpt/db cat > .env <<EOF POSTGRES_PASSWORD=$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n') PAPERLESS_SECRET_KEY=$(od -An -N48 -tx1 /dev/urandom | tr -d ' \n') USERMAP_UID=$(id -u) USERMAP_GID=$(id -g) EOF chmod 600 .env
Windows (PowerShell)#
$Root = Join-Path $HOME "docker_volumes\paperless-ngx"
New-Item -ItemType Directory -Force -Path $Root | Out-Null
Set-Location $Root
"data","media","consume","export","postgres","broker","gpt\prompts","gpt\config","gpt\db" |
ForEach-Object { New-Item -ItemType Directory -Force -Path $_ | Out-Null }
function New-RandomHex([int]$Count) {
$Bytes = New-Object byte[] $Count
$Rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
try { $Rng.GetBytes($Bytes) } finally { $Rng.Dispose() }
($Bytes | ForEach-Object { $_.ToString("x2") }) -join ""
}
$EnvText = @"
POSTGRES_PASSWORD=$(New-RandomHex 32)
PAPERLESS_SECRET_KEY=$(New-RandomHex 48)
USERMAP_UID=1000
USERMAP_GID=1000
"@
[System.IO.File]::WriteAllText((Join-Path $Root ".env"), $EnvText, (New-Object System.Text.UTF8Encoding($false)))
On Windows the USERMAP values only describe the user inside the container. Windows file sharing in Docker Desktop still decides who can read the folder. Keep the folder inside your own profile.
Mac (Terminal)#
Same commands as Linux:
set -eu umask 077 mkdir -p "$HOME/docker_volumes/paperless-ngx" cd "$HOME/docker_volumes/paperless-ngx" mkdir -p data media consume export postgres broker gpt/prompts gpt/config gpt/db cat > .env <<EOF POSTGRES_PASSWORD=$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n') PAPERLESS_SECRET_KEY=$(od -An -N48 -tx1 /dev/urandom | tr -d ' \n') USERMAP_UID=$(id -u) USERMAP_GID=$(id -g) EOF chmod 600 .env
Check it worked: ls -a (Windows: Get-ChildItem -Force) shows .env and the folders. Do not print the file in a shared screen.
Potential issue: Docker Desktop must be allowed to share your home folder (Mac: Settings, Resources, File sharing).
The Compose file (same on all systems)#
This is the official PostgreSQL + Tika file, adapted: pinned image, bind mounts under your home folder, ports on localhost only, and no version: line.
Save it as docker-compose.yml in the same folder. The tags for Valkey, PostgreSQL, Gotenberg and Tika match the official file for v3.3.4 and each tag exists on its registry. PostgreSQL 18 mounts /var/lib/postgresql (not the older /var/lib/postgresql/data). Never point a new PostgreSQL major version at a data folder made by an older one.
name: paperless
services:
broker:
image: docker.io/valkey/valkey:9-alpine
restart: unless-stopped
volumes:
- ./broker:/data
db:
image: docker.io/library/postgres:18
restart: unless-stopped
volumes:
- ./postgres:/var/lib/postgresql
environment:
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:3.3.4
restart: unless-stopped
depends_on:
- db
- broker
- gotenberg
- tika
ports:
- "127.0.0.1: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_DBNAME: paperless
PAPERLESS_DBUSER: paperless
PAPERLESS_DBPASS: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY:?Set PAPERLESS_SECRET_KEY}
PAPERLESS_URL: http://localhost:8000
USERMAP_UID: ${USERMAP_UID:-1000}
USERMAP_GID: ${USERMAP_GID:-1000}
PAPERLESS_TIME_ZONE: UTC
PAPERLESS_OCR_LANGUAGE: eng
PAPERLESS_OCR_MODE: auto
PAPERLESS_TASK_WORKERS: "1"
PAPERLESS_THREADS_PER_WORKER: "2"
PAPERLESS_CONSUMER_POLLING_INTERVAL: "10"
PAPERLESS_TIKA_ENABLED: "1"
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: docker.io/gotenberg/gotenberg:8.37
restart: unless-stopped
command:
- gotenberg
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=^file:///tmp/.*"
tika:
image: docker.io/apache/tika:3.3.1.0
restart: unless-stopped
What the settings do:
PAPERLESS_OCR_LANGUAGEpicks the language Tesseract reads. The container already ships English, German, Italian, Spanish and French.PAPERLESS_OCR_MODE: autois the v3 default. A document that already has enough embedded text skips OCR, and one without text is OCRed. (The oldskipvalue from v2 is now treated asauto.)PAPERLESS_CONSUMER_POLLING_INTERVAL: "10"checks the consume folder every 10 seconds instead of waiting for file-change events (the default0). It helps on network shares. The v2 namePAPERLESS_CONSUMER_POLLINGno longer works in v3.- Change the time zone to yours, for example
Asia/Tokyo. - The two Gotenberg flags come from the official file: they stop email conversion from loading outside content or running JavaScript.
Windows note: a PostgreSQL bind mount on an NTFS folder can fail with ownership errors (VERIFY on your setup). If that happens, either replace ./postgres:/var/lib/postgresql with a named volume (add pgdata: under volumes: at the bottom) or run the Linux steps inside a WSL distribution and keep everything on its Linux disk. Do not switch the same database between the two.
Step 2: Start the stack and create your admin#
Check the file, pull the images, start everything, then create the first user.
Linux#
cd "$HOME/docker_volumes/paperless-ngx" docker compose config --quiet docker compose pull docker compose up -d docker compose ps docker compose logs --tail=100 webserver docker compose exec webserver createsuperuser
Windows (PowerShell)#
Set-Location "$HOME\docker_volumes\paperless-ngx" docker compose config --quiet docker compose pull docker compose up -d docker compose ps docker compose logs --tail=100 webserver docker compose exec webserver createsuperuser
Mac (Terminal)#
Same commands as Linux:
cd "$HOME/docker_volumes/paperless-ngx" docker compose config --quiet docker compose pull docker compose up -d docker compose ps docker compose logs --tail=100 webserver docker compose exec webserver createsuperuser
Check it worked: docker compose ps lists webserver, db, broker, gotenberg and tika as running, and http://localhost:8000 shows the login page. The first start runs the database migrations, so give it a minute. createsuperuser asks for a name and password interactively, so no admin password sits in a config file.
Potential issue: if the web page does not load, read docker compose logs --tail=100 webserver. Keep the container command: as it is. A custom command: that replaces the long-running one caused the one restart-loop report we found for 3.3.4.
First-run setup#
Test with three real files before you import a whole archive.
- Sign in at http://localhost:8000 with your admin.
- Upload one PDF scan, one Word or Excel file and one normal text PDF through the web page.
- Open each document. Is the text searchable? Is the page the right way up? Can you download the original and the archived copy?
- Create a few tags, correspondents and document types. Paperless can also learn rules from your reviewed documents (automatic matching), with no AI at all.
- Create normal users for everyday work (Settings, Users and groups) and use the admin only for setup. Set owners and sharing on purpose, then sign in as a normal user and check what that user sees.
The consume folder#
Copy files into ~/docker_volumes/paperless-ngx/consume and Paperless picks them up. Inside the container this folder is /usr/src/paperless/consume. To move it, change the host side of the volume line, not the setting PAPERLESS_CONSUMPTION_DIR.
Treat consume as a queue. Paperless needs permission to remove files it has consumed, so keep your own copy until the document is safe in a backup. If your scanner writes slowly, let it write to another folder and move finished files in.
More OCR languages#
Set PAPERLESS_OCR_LANGUAGE to the language codes you read (for example eng+deu). If a language is not one of the five built in, also set PAPERLESS_OCR_LANGUAGES to the extra pack names, for example PAPERLESS_OCR_LANGUAGES: jpn, and recreate the container. Every extra language costs CPU, so list only the ones you really have. Settings you change in the web page can override these environment values.
Mail fetching#
Add your mailbox under Mail accounts, then create a mail rule. Start narrow: one folder, one sender, a non-destructive action. Paperless checks mail every 10 minutes. To test now:
docker compose exec webserver mail_fetcher
That command is the same in all three shells. Gmail and Microsoft accounts need token-based sign-in, and the exact steps are provider-specific (VERIFY in your provider's docs). The PAPERLESS_EMAIL_HOST settings are for outgoing mail only.
Add local AI tagging with paperless-gpt and Ollama (optional)#
paperless-gpt reads the text Paperless already has, asks a local model for a title, tags and a correspondent, and waits for your approval.
Start small. Keep the normal OCR, keep PDF_REPLACE off, and review every suggestion by hand at first. If you do not want any AI, skip this step. Paperless works fine without it.
You need Ollama running on your machine with a model pulled. Install it from the official Ollama site, then:
ollama pull qwen3:8b ollama list
The model name comes from the paperless-gpt README example. Smaller or larger models work too. Quality, especially for non-English text, depends on the model, so test your own documents.
- In Paperless, create a user just for the helper and give it access only to the documents it should process. Open that user's profile and create an API token.
- Add this line to
.envwith the real token:
PAPERLESS_GPT_TOKEN=<YOUR_API_TOKEN>
- Add this service under
services:indocker-compose.yml:
paperless-gpt:
image: icereed/paperless-gpt:v0.31.0
restart: unless-stopped
depends_on:
- webserver
ports:
- "127.0.0.1:8080:8080"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
PAPERLESS_BASE_URL: http://webserver:8000
PAPERLESS_PUBLIC_URL: http://localhost:8000
PAPERLESS_API_TOKEN: ${PAPERLESS_GPT_TOKEN:?Set PAPERLESS_GPT_TOKEN}
LLM_PROVIDER: ollama
LLM_MODEL: qwen3:8b
OLLAMA_HOST: http://host.docker.internal:11434
OLLAMA_CONTEXT_LENGTH: "8192"
OLLAMA_KEEP_ALIVE: 10m
OLLAMA_THINK: "false"
LLM_LANGUAGE: English
MANUAL_TAG: paperless-gpt
AUTO_TAG: paperless-gpt-auto
PDF_UPLOAD: "false"
PDF_REPLACE: "false"
volumes:
- ./gpt/prompts:/app/prompts
- ./gpt/config:/app/config
- ./gpt/db:/app/db
Keep all three mounted folders. The project warns that without them you lose prompts, settings and history when the container is replaced.
- Start it on every system with the same commands:
docker compose pull paperless-gpt docker compose up -d paperless-gpt docker compose logs --tail=100 paperless-gpt
- Open http://localhost:8080. Add the tag
paperless-gptto one test document in Paperless, generate suggestions in paperless-gpt, read them, then apply. Check that nothing else on the document changed. Only after a few good rounds try thepaperless-gpt-autotag on a small batch.
Check it worked: the log shows the helper connected to Paperless, and the suggestions page lists your tagged test document.
Things to know:
- No login on port 8080. paperless-gpt has no authentication, and by default it listens on all interfaces. Anyone who reaches the port can change documents in Paperless. That is why the Compose line binds it to
127.0.0.1. - Ollama must be reachable from the container. On Docker Desktop (Windows and Mac)
host.docker.internalworks. On Linux theextra_hostsline above provides it, but Ollama listens only on the host loopback by default, so the container may not reach it until you change how Ollama binds (VERIFY with the current Ollama docs). Do not open that port to your network just to make this work. - The README says local does not mean offline by itself: the helper can also talk to cloud providers if you configure them. Check every endpoint you set.
- Run only one AI tagger against your documents at a time while you test.
Put it behind HTTPS with Traefik#
On a server with a domain, let Traefik handle HTTPS and drop the published ports. Local-only users can skip this.
This is an illustration, not an official Paperless example. It assumes Traefik v3 already runs with the Docker provider, an external network called proxy, an entrypoint called https and a certificate for your host name. Create the network once if needed: docker network create proxy. Then change the webserver service like this (merge these keys, do not add a second service):
webserver:
# keep image, depends_on, volumes and the other settings as above
environment:
PAPERLESS_URL: https://paperless.<your-domain>
PAPERLESS_ALLOWED_HOSTS: paperless.<your-domain>,localhost,webserver
PAPERLESS_PROXY_SSL_HEADER: '["HTTP_X_FORWARDED_PROTO", "https"]'
networks:
- default
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.paperless.rule=Host(`paperless.<your-domain>`)"
- "traefik.http.routers.paperless.entrypoints=https"
- "traefik.http.routers.paperless.tls=true"
- "traefik.http.services.paperless.loadbalancer.server.port=8000"
networks:
proxy:
external: true
Remove the ports: lines from webserver when Traefik is in front. Add traefik.http.routers.paperless.tls.certresolver=<your-resolver> if you use an ACME resolver. PAPERLESS_URL is required when you use a public domain, and the proxy header setting is only safe when the header comes from your own proxy.
Do not give paperless-gpt a Traefik route unless the proxy also asks for a login. HTTPS alone does not protect it.
Security and testing#
Paperless-ngx needs a strong secret key, no direct internet exposure and a locked .env; the AI helper needs a network that only you can reach.
| Control | How |
|---|---|
| Secrets | .env with chmod 600, never in Git; keep a safe copy of PAPERLESS_SECRET_KEY |
| Network | Ports on 127.0.0.1 only; database, queue, Tika and Gotenberg publish nothing |
| Remote access | A VPN or a private network first; a public domain only with a proxy and good passwords |
| Users | Daily work with normal users, admin only for setup |
| AI helper | Its own limited Paperless user and token; never exposed without a login in front |
| Disk | Paperless does not encrypt stored documents; use an encrypted disk against theft |
| Auto login | Leave the automatic-login and remote-user header options off |
Quick tests after every change:
docker compose ps docker compose logs --tail=100 webserver docker compose exec webserver document_sanity_checker
The sanity checker looks for missing files and wrong checksums. For a Traefik setup also run curl -I https://paperless.<your-domain>/.
Troubleshooting#
| Problem | Solution |
|---|---|
| Web container restarts again and again after a Granian shutdown message | Remove any custom command: from webserver; it replaces the real start command |
chown: changing ownership of '../export': Operation not permitted | The storage (often a network share) forbids ownership changes; use a local disk or fix the share, not chmod 777 |
[Errno 13] Permission denied on a consume file | Set USERMAP_UID and USERMAP_GID to your user, and make sure the container can read, write and delete in consume |
504 Server Error: Gateway Timeout for url: http://gotenberg:3000/forms/libreoffice/convert | Add "--api-timeout=60s" to the Gotenberg command: list and keep the other flags |
| A file is picked up half-written | Let the scanner write to another folder and move finished files into consume; keep polling on |
Classifier error: No training data available | Harmless with no automatic matching; otherwise review some documents so the classifier can learn |
Ghostscript PDF/A rendering failed | Check the source PDF; the opt-in continue_on_soft_render_error setting helps, but check the archived copy |
| Search results look wrong | docker compose exec webserver document_index reindex |
| paperless-gpt cannot reach Ollama | Check OLLAMA_HOST, that the model is pulled, and the Linux loopback note in the AI section |
Diagnostics: docker compose ps, then docker compose logs --tail=200 webserver (also db, gotenberg, paperless-gpt).
Granian shutdown completed, see ya!#
In an October 2026 report for 3.3.4 the web container printed Granian shutdown completed, see ya! and started again in a loop. The cause was a Compose file that replaced the container command to install jq. Remove the override. If you need extra tools, use a custom init script as the official docs describe.
Import into a half-filled target#
A report from the same week showed FileNotFoundError: [Errno 2] No such file or directory: '/opt/paperless_data/data/index', then a FileExistsError on retry. It came from a third-party non-Docker script, and the maintainers said not to import into partly filled storage. Always import into a fresh, empty install.
Known bugs#
None confirmed for 3.3.4 beyond the two reports above. paperless-gpt v0.31.0 fixed a case where fewer pages were processed than the document had (image OCR mode), so stay on v0.31.0 or newer.
Updates and backups#
Export with document_exporter, copy it off the machine, prove it with a restore test, then change the pinned tag.
The export holds documents, thumbnails, metadata and the database contents. It does not hold API tokens, so recreate those after an import. Keep .env, docker-compose.yml and the gpt folder as a separate backup.
Pause scanning, mail and the AI helper first so nothing changes during the export.
Linux#
cd "$HOME/docker_volumes/paperless-ngx" BACKUP="backup-$(date +%Y%m%d-%H%M%S)" docker compose exec -T webserver document_exporter "/usr/src/paperless/export/$BACKUP" docker compose exec -T webserver document_sanity_checker
Windows (PowerShell)#
Set-Location "$HOME\docker_volumes\paperless-ngx" $Backup = "backup-" + (Get-Date -Format "yyyyMMdd-HHmmss") docker compose exec -T webserver document_exporter "/usr/src/paperless/export/$Backup" docker compose exec -T webserver document_sanity_checker
Mac (Terminal)#
Same commands as Linux:
cd "$HOME/docker_volumes/paperless-ngx" BACKUP="backup-$(date +%Y%m%d-%H%M%S)" docker compose exec -T webserver document_exporter "/usr/src/paperless/export/$BACKUP" docker compose exec -T webserver document_sanity_checker
The export lands in export/ on your disk. Copy it to another disk or machine. Schedule it with cron at 03:00 on Linux and Mac (0 3 * * *) or with Task Scheduler on Windows. An export on the same disk does not protect you from that disk dying.
Restore test#
The importer needs a completely empty Paperless, and the official docs advise importing with the same version that made the export. Test it in a second folder so production stays untouched:
- Make
~/docker_volumes/paperless-restore-test, copydocker-compose.ymlthere, and changename: paperlesstoname: paperless-restore-test. - Make new empty folders and a new
.envwith new secrets (Step 1). Change the host port to127.0.0.1:8001:8000andPAPERLESS_URLtohttp://localhost:8001. - Remove the
paperless-gptservice, and addPAPERLESS_EMAIL_TASK_CRON: "disable"towebserverso the test cannot fetch your mail. - Copy one export folder to
export/restore-input. - Start the helpers and import. Do not create a superuser first, because the export contains the users.
docker compose up -d db broker gotenberg tika docker compose run --rm --no-deps webserver document_importer /usr/src/paperless/export/restore-input docker compose up -d webserver docker compose exec -T webserver document_sanity_checker
These commands are the same in all three shells. VERIFY the one-off importer on your own setup. If an import fails halfway, keep the logs, delete the test folders and start again from empty. Then open http://localhost:8001 and check the document count, a few downloads, search, tags and users. A command that exits without error is not proof; the documents must open.
Update#
- Read the release notes and the newest issues.
- Pause scanning, mail and AI. Export and check a backup.
- Change only the image tag on
webserver, then:
docker compose down docker compose pull webserver docker compose up -d docker compose logs --tail=200 webserver docker compose exec -T webserver document_sanity_checker
Start-up applies the database migrations. Never run docker compose down -v as part of an update, and do not change the PostgreSQL major version in the same step. Rolling back means restoring the backup, because migrations are not reversed by choosing an older tag.
Performance and resources#
Start with one worker and two threads, measure, then raise them, and never go past your CPU core count.
We did not run this stack, so we give no measured memory number (see docker stats on your machine). What the docs say:
- Workers times threads must stay within your cores. On one core use one thread.
- More OCR languages mean more CPU. List only the ones you have.
automode avoids OCR on documents that already have enough embedded text.- Keep the data on a local disk; some network shares cause ownership errors.
- For the AI helper, a longer context and a bigger model need more memory.
OLLAMA_KEEP_ALIVEkeeps the model loaded between requests, andOLLAMA_TIMEOUT_SECONDS(default 300) covers slow answers. No source gives a pages-per-minute figure, so time your own documents.
Watch it with:
docker stats docker compose logs --tail=100 webserver
FAQ#
Does Paperless-ngx need AI to be useful? No. OCR, full-text search, matching rules and the built-in classifier work without paperless-gpt or Ollama. Add the AI helper only if you want better titles and tags.
Can it read more than one language? Yes. Use codes like eng+deu in PAPERLESS_OCR_LANGUAGE and install any extra packs with PAPERLESS_OCR_LANGUAGES. Test a mixed-language page before you import a lot.
Does a local model mean nothing leaves my network? Not automatically. paperless-gpt can also call cloud providers if you set them. Check every endpoint in your Compose file.
Can I throw away the paper after scanning? This guide cannot tell you that. Rules for tax, insurance and contract papers depend on your country, so check them first. Keep the originals until you know.
Is the export a full backup? It is the main one, but it leaves out API tokens, your .env and the gpt folder. Back those up too and run a restore test.
Sources#
- Paperless-ngx documentation (setup, configuration, administration and troubleshooting pages, read 2026-10-26; the pages carry no publication date)
- Paperless-ngx v3.3.4 release (22 October 2026)
- Official PostgreSQL + Tika Compose file and env file (v3.3.4, read 2026-10-26)
- Issue 14537, discussion 14538 and issue 14543 (23-24 October 2026)
- paperless-gpt README and releases (v0.31.0, 14 October 2026)
- paperless-ai README (not-maintained notice, read 2026-10-26)
- Image tags confirmed on ghcr.io and Docker Hub on 2026-10-26: paperless-ngx 3.3.4, paperless-gpt v0.31.0, postgres 18, valkey 9-alpine, gotenberg 8.37, tika 3.3.1.0
- Checked against the official docs on 2026-10-26. The commands were not run on Linux, Windows or Mac; items marked VERIFY are open