Paperless-ngx zelf hosten op een VPS met Docker Compose
Deploy Paperless-ngx op een VPS met Docker Compose, PostgreSQL en Redis. Configureer OCR-talen en -modi, automatische tagging-regels, e-mailverwerking en een productie-backupstrategie met offsite-synchronisatie.
Paperless-ngx maakt van je VPS een doorzoekbaar documentenarchief. Je scant of uploadt documenten, Paperless-ngx voert OCR uit, maakt ze doorzoekbaar op volledige tekst en ordent ze met automatische tags en correspondenten. Het draait als een Docker Compose-stack met PostgreSQL, Redis en Gotenberg voor documentconversie.
Deze handleiding deployt Paperless-ngx op een VPS waar Docker en een reverse proxy al draaien. Als je die basis nog niet hebt, begin dan met onze handleiding voor het opzetten van de Docker-basisstack.
Welke resources heeft Paperless-ngx nodig op een VPS?
Paperless-ngx vereist minimaal 2 GB RAM, 4 GB aanbevolen. De stack draait PostgreSQL, Redis, Gotenberg, Tika en de webserver. In rust ligt het totale geheugengebruik rond de 800 MB. Tijdens OCR-verwerking van gescande PDF's schiet het CPU-gebruik naar 100% op één core en stijgt het RAM-gebruik naar 1,5-2 GB. Schijfgebruik is gemiddeld 5-10 MB per document (origineel + archief + thumbnail).
| Component | RAM (rust) | RAM max (OCR) | Schijf per 1.000 docs |
|---|---|---|---|
| Paperless-ngx webserver | ~300 MB | ~900 MB | 5-10 GB |
| PostgreSQL | ~50 MB | ~100 MB | ~500 MB |
| Redis | ~10 MB | ~10 MB | verwaarloosbaar |
| Gotenberg | ~150 MB | ~300 MB | — |
| Tika | ~250 MB | ~400 MB | — |
| Totaal | ~760 MB | ~1.710 MB | 5-10 GB |
Plan de schijfruimte op basis van je documentvolume. Een huishouden dat 50 documenten per maand scant, heeft ongeveer 6 GB per jaar nodig. Een klein bedrijf met 500 documenten/maand moet 50-60 GB per jaar reserveren.
Hoe deploy ik Paperless-ngx met Docker Compose op een VPS?
Maak een directory voor de stack, genereer secrets en schrijf het Compose-bestand. Elke service heeft een health check zodat Docker ongezonde containers automatisch herstart.
Projectdirectory aanmaken
mkdir -p /opt/paperless-ngx && cd /opt/paperless-ngx
Secrets genereren
Gebruik nooit standaardwachtwoorden. Genereer een sterk databasewachtwoord en een Django-secret-key:
openssl rand -base64 32 > .db_password
openssl rand -base64 48 > .secret_key
chmod 600 .db_password .secret_key
Deze bestanden blijven op schijf staan met beperkte rechten. Het Compose-bestand leest ze bij het starten van de containers.
Omgevingsbestand schrijven
cat > .env << 'EOF'
COMPOSE_PROJECT_NAME=paperless
EOF
Compose-bestand schrijven
# docker-compose.yml
services:
broker:
image: docker.io/library/redis:8
restart: unless-stopped
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 5s
retries: 3
db:
image: docker.io/library/postgres:18
restart: unless-stopped
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U paperless"]
interval: 30s
timeout: 5s
retries: 3
gotenberg:
image: docker.io/gotenberg/gotenberg:8
restart: unless-stopped
command:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
tika:
image: docker.io/apache/tika:latest
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9998/"]
interval: 30s
timeout: 5s
retries: 3
webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
broker:
condition: service_healthy
gotenberg:
condition: service_healthy
tika:
condition: service_healthy
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_DBUSER: paperless
PAPERLESS_DBPASS_FILE: /run/secrets/db_password
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
PAPERLESS_SECRET_KEY_FILE: /run/secrets/secret_key
PAPERLESS_OCR_LANGUAGE: eng
PAPERLESS_OCR_MODE: skip
PAPERLESS_OCR_OUTPUT_TYPE: pdfa
PAPERLESS_FILENAME_FORMAT: "{created_year}/{correspondent}/{title}"
PAPERLESS_URL: https://paperless.example.com
USERMAP_UID: 1000
USERMAP_GID: 1000
secrets:
- db_password
- secret_key
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
secrets:
db_password:
file: .db_password
secret_key:
file: .secret_key
volumes:
data:
media:
pgdata:
redisdata:
Over deze configuratie:
- Poorten zijn alleen gebonden aan 127.0.0.1. Je reverse proxy verzorgt de publieke toegang via HTTPS. Poort 8000 blootstellen op
0.0.0.0zou TLS en je firewall omzeilen. - Secrets gebruiken Docker secrets (variabelen met het
_FILE-achtervoegsel). Wachtwoorden verschijnen nooit in de uitvoer vandocker inspectof in proceslijsten. USERMAP_UID/USERMAP_GIDkoppelen de containergebruiker aan UID 1000 op de host. Bestanden die in de consume- en export-directory's worden aangemaakt, zijn eigendom van deze gebruiker.PAPERLESS_URLmoet overeenkomen met je publieke domein. Paperless-ngx gebruikt dit voor het genereren van deellinks en e-mail-URL's. Vervangpaperless.example.comdoor je werkelijke domein.
De stack starten
docker compose up -d
Wacht ongeveer 60 seconden tot alle services zijn geïnitialiseerd. Controleer of elke container gezond is:
docker compose ps
NAME SERVICE STATUS PORTS
paperless-broker-1 broker running (healthy)
paperless-db-1 db running (healthy)
paperless-gotenberg-1 gotenberg running (healthy)
paperless-tika-1 tika running (healthy)
paperless-webserver-1 webserver running (healthy) 127.0.0.1:8000->8000/tcp
Alle vijf containers moeten (healthy) tonen. Als er een (health: starting) toont, wacht dan nog 30 seconden. Als een container (unhealthy) blijft, bekijk de logs:
docker compose logs gotenberg --tail 20
Superuser aanmaken
docker compose exec webserver createsuperuser
Volg de prompts voor gebruikersnaam, e-mail en wachtwoord. Dit is je beheerdersaccount voor de webinterface.
Toegang tot de webinterface
Als je reverse proxy is geconfigureerd, open https://paperless.example.com in een browser. De Paperless-ngx-inlogpagina wordt geladen. Als je de reverse proxy nog aan het instellen bent, test lokaal:
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8000
200
Welke OCR-talen en -modi moet ik configureren?
Paperless-ngx gebruikt Tesseract voor OCR. Taal en verwerkingsmodus configureer je via omgevingsvariabelen. De standaardwaarden werken voor Engelstalige documenten, maar de meeste gebruikers moeten ze aanpassen.
OCR-taal
Stel PAPERLESS_OCR_LANGUAGE in op de drieletterige Tesseract-taalcode van je documenten. Voor meerdere talen, verbind ze met +:
# Enkele taal
PAPERLESS_OCR_LANGUAGE: nld
# Meerdere talen
PAPERLESS_OCR_LANGUAGE: nld+eng+deu
Veelgebruikte taalcodes: eng (Engels), deu (Duits), fra (Frans), spa (Spaans), ita (Italiaans), nld (Nederlands), por (Portugees). De volledige lijst staat in de Tesseract-documentatie.
De containerimage bevat de meeste taalpakketten. Meer talen toevoegen aan PAPERLESS_OCR_LANGUAGE verhoogt de OCR-verwerkingstijd per pagina.
OCR-modi
De instelling PAPERLESS_OCR_MODE bepaalt hoe Paperless-ngx omgaat met documenten die al een tekstlaag bevatten (gebruikelijk bij digitaal gemaakte PDF's).
| Modus | Gedrag | Wanneer gebruiken |
|---|---|---|
skip |
OCR alleen op pagina's zonder tekst. Maakt altijd een archiefkopie. | Standaard. Ideaal voor gemengde invoer: gescande + digitale PDF's. |
skip_noarchive |
Zoals skip, maar zonder archiefkopie voor documenten die al tekst hebben. |
Je wilt schijfruimte besparen bij digitale PDF's. |
redo |
Alle pagina's opnieuw OCR'en, bestaande tekstlagen vervangen. | Je ontvangt documenten met slechte OCR-kwaliteit. |
force |
Document rasteriseren en OCR vanaf nul. Vernietigt de originele tekst. | Laatste redmiddel. Produceert grotere bestanden met minder scherpe tekst. |
Voor de meeste opstellingen is skip de juiste keuze. Het verwerkt zowel gescande als digitale documenten zonder CPU te verspillen aan het herverwerken van al doorzoekbare documenten.
Als je documenten ontvangt van een scanner die zijn eigen (slechte) OCR uitvoert, zet de modus dan op redo. Let op: redo is niet compatibel met PAPERLESS_OCR_CLEAN en PAPERLESS_OCR_DESKEW.
OCR-uitvoertype
PAPERLESS_OCR_OUTPUT_TYPE: pdfa (de standaard) produceert PDF/A-bestanden, de archiveringsstandaard. Deze zijn zelfstandig en bevatten ingebedde lettertypen. Houd deze instelling aan, tenzij je een specifieke reden hebt om te wijzigen.
Na het wijzigen van OCR-instellingen, herstart de webserver-container:
docker compose restart webserver
Bestaande documenten worden niet opnieuw verwerkt. Om een document opnieuw te OCR'en, gebruik de actie «Redo OCR» in de webinterface.
Hoe werkt automatische tagging in Paperless-ngx?
Paperless-ngx ondersteunt zes matching-algoritmen voor tags, correspondenten en documenttypen. Wanneer een document binnenkomt, vergelijkt elke tag de documentinhoud met het eigen patroon. Als het patroon overeenkomt, wordt de tag automatisch toegepast.
| Algoritme | Hoe het werkt | Wanneer gebruiken | Voorbeeld |
|---|---|---|---|
| Any | Komt overeen als een woord uit het matchveld voorkomt. | Brede categorieën. | invoice receipt bill |
| All | Komt overeen als alle woorden voorkomen (willekeurige volgorde). | Nauwkeurigere matching zonder positieafhankelijkheid. | electricity quarterly |
| Exact | Komt exact overeen met de zin in volgorde. | Bedrijfsnamen, rekeningnummers. | Acme Corp |
| Regular Expression | Volledige regex-patroonmatching. | Gestructureerde data: datums, referentienummers, bedragen. | Invoice\s+#?\d{4,} |
| Fuzzy | Benadering met configureerbare drempelwaarde. | Inconsistente OCR-uitvoer, lichte spelfouten. | Stadtwerke (herkent Stadtwenke) |
| Auto | ML-classifier die leert van je handmatige toewijzingen. | Na ~50+ handmatig getagde documenten. | (leert van je correcties) |
Tags met matchingregels aanmaken
Ga in de webinterface naar Manage > Tags en maak een tag aan. Stel het matching-algoritme en het Match-veld in. Enkele praktische voorbeelden:
- Tag: Invoice, Algoritme:
Any, Match:invoice rechnung facture factuur(vangt facturen in meerdere talen) - Tag: Bank, Algoritme:
Regular Expression, Match:IBAN\s*[A-Z]{2}\d{2}(herkent elk IBAN-nummer) - Tag: Medical, Algoritme:
All, Match:patient diagnosis(vereist beide woorden) - Correspondent: Electric Company, Algoritme:
Exact, Match:Springfield Energy Inc
De Auto-classifier trainen
Het Auto-algoritme gebruikt een machine learning-classifier die Paperless-ngx automatisch hertraint. Om hem te trainen:
- Tag handmatig minstens 50 documenten verdeeld over je categorieën
- Zorg dat deze documenten niet in je inbox staan (de classifier negeert inbox-documenten)
- De classifier hertraint volgens een schema (standaard: elk uur via
document_create_classifier) - Nieuwe documenten beginnen automatische toewijzingen te ontvangen
Je kunt handmatig hertrainen:
docker compose exec webserver document_create_classifier
De classifier verbetert naarmate je correcties aanbrengt. Elke correctie voedt de volgende trainingscyclus.
Hoe stel ik documentverwerking in voor Paperless-ngx?
Paperless-ngx verwerkt documenten via drie kanalen: handmatige upload via de webinterface, een bewaakte map op schijf en e-mail ophalen via IMAP.
Upload via de webinterface
Sleep bestanden naar de webinterface. Dit werkt direct na de installatie. Ondersteunde formaten: PDF, PNG, JPEG, TIFF en (met Tika ingeschakeld) DOCX, XLSX, ODT en andere kantoorformaten.
Bewaakte map (consume-directory)
De ./consume-directory die in het Compose-bestand is gemount, wordt bewaakt door een inotify-watcher. Plaats een bestand erin en Paperless-ngx pikt het binnen seconden op.
cp /tmp/scan-001.pdf /opt/paperless-ngx/consume/
Het bestand verdwijnt uit de consume-directory zodra de verwerking is voltooid. Om de verwerking per tag te organiseren, schakel subdirectory-tagging in:
PAPERLESS_CONSUMER_RECURSIVE: true
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS: true
Met deze instelling krijgt een bestand in consume/invoices/scan.pdf automatisch de tag invoices.
E-mailverwerking (IMAP)
E-mailverwerking wordt volledig geconfigureerd in de webinterface onder Manage > Mail. Je voegt mailaccounts en mailregels toe.
Een mailaccount toevoegen:
- Ga naar Manage > Mail > Mail Accounts
- Vul de IMAP-server, poort (993 voor TLS), gebruikersnaam en wachtwoord in
- Stel de te bewaken map in (bijv.
INBOX)
Een mailregel toevoegen:
- Ga naar Manage > Mail > Mail Rules
- Selecteer het mailaccount
- Kies wat te verwerken: alleen bijlagen of de volledige e-mail als PDF
- Stel een actie in voor verwerkte e-mails: markeer als gelezen, verplaats naar een map of verwijder
- Optioneel wijs een tag, correspondent of documenttype toe
Typische opzet: stuur documenten door naar een specifiek e-mailadres (bijv. scan@jouwdomein.nl), configureer Paperless-ngx om die inbox te bewaken en bijlagen te verwerken. De originelen worden na verwerking verplaatst naar een IMAP-map «Archived».
Hoe organiseer ik opslagpaden en bestandsnamen?
Paperless-ngx slaat documenten op twee plaatsen op: het media-volume bevat de bestanden op schijf, terwijl PostgreSQL de metadata beheert. De variabele PAPERLESS_FILENAME_FORMAT bepaalt hoe gearchiveerde bestanden worden benoemd in de media-directory.
Het Compose-bestand hierboven gebruikt:
PAPERLESS_FILENAME_FORMAT: "{created_year}/{correspondent}/{title}"
Dit creëert een structuur als:
media/documents/archive/
2026/
Springfield Energy Inc/
Electricity Bill January 2026.pdf
Dr. Smith/
Lab Results March 2026.pdf
Beschikbare templatevariabelen zijn {created_year}, {created_month}, {created_day}, {correspondent}, {document_type}, {title}, {tag_list} en {owner_username}. Als een variabele leeg is (geen correspondent toegewezen), vervangt Paperless-ngx deze door none.
Na het wijzigen van het bestandsnaamformaat, pas het toe op bestaande documenten:
docker compose exec webserver document_renamer
Wat is de beste backupstrategie voor Paperless-ngx?
Een werkende backup omvat de PostgreSQL-database, de documentbestanden (originelen + archieven + thumbnails) en de Paperless-ngx-metadata (tags, correspondenten, matchingregels). De documentexporter legt dit alles vast in een draagbaar formaat. Combineer het met een databasedump voor snellere database-only restores.
Databasedump
docker compose exec db pg_dump -U paperless paperless | gzip > /opt/paperless-ngx/backups/db-$(date +%Y%m%d).sql.gz
Documentexporter
De exporter maakt een volledige snapshot: documenten, metadata en manifest. Dit is de standaard backupmethode.
docker compose exec webserver document_exporter ../export -c -d
De -c-vlag vergelijkt checksums (exporteert alleen gewijzigde bestanden). De -d-vlag verwijdert bestanden uit de export die niet meer bestaan in Paperless-ngx. Samen houden ze de export-directory als spiegel van de huidige staat.
Automatiseren met cron
Maak een backupscript:
cat > /opt/paperless-ngx/backup.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/opt/paperless-ngx/backups"
EXPORT_DIR="/opt/paperless-ngx/export"
mkdir -p "$BACKUP_DIR"
# Database dump
docker compose -f /opt/paperless-ngx/docker-compose.yml exec -T db \
pg_dump -U paperless paperless | gzip > "$BACKUP_DIR/db-$(date +%Y%m%d).sql.gz"
# Document exporter
docker compose -f /opt/paperless-ngx/docker-compose.yml exec -T webserver \
document_exporter ../export -c -d
# Remove database dumps older than 30 days
find "$BACKUP_DIR" -name "db-*.sql.gz" -mtime +30 -delete
echo "[$(date -Is)] Backup completed" >> "$BACKUP_DIR/backup.log"
SCRIPT
chmod 700 /opt/paperless-ngx/backup.sh
Plan het in met cron (dit behoudt bestaande crontab-items):
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/paperless-ngx/backup.sh") | crontab -
Dit voegt een nachtelijke taak toe om 03:00 uur.
Offsite-synchronisatie
De export-directory en databasedumps moeten de server verlaten. Gebruik rsync of rclone om ze naar een tweede locatie te sturen. Voor een S3-compatibel doel:
rclone sync /opt/paperless-ngx/export remote:paperless-backup/export
rclone sync /opt/paperless-ngx/backups remote:paperless-backup/db-dumps
Voor een andere server via SSH:
rsync -az --delete /opt/paperless-ngx/export/ backup-server:/backups/paperless/export/
rsync -az /opt/paperless-ngx/backups/ backup-server:/backups/paperless/db-dumps/
Voor een diepere duik in Docker volume-backupstrategieën, zie onze handleiding voor Docker-volume-backupstrategieën.
Hoe herstel ik een Paperless-ngx-backup?
Herstellen vereist een schone Paperless-ngx-installatie. Start de stack en importeer dan.
Alleen database herstellen (sneller, bij databasecorruptie of migratie):
gunzip < /opt/paperless-ngx/backups/db-20260320.sql.gz | \
docker compose exec -T db psql -U paperless paperless
Volledig herstel vanuit de exporter (documenten, tags, correspondenten, alles):
docker compose exec webserver document_importer ../export
Voer de importer uit tegen een lege database. Het herstelt alle metadata en koppelt bestanden uit de export-directory.
Test je herstel regelmatig. Een backup die je nooit hebt hersteld, is geen backup.
Hoe beveilig ik de Paperless-ngx-containers extra?
Het Compose-bestand hierboven dekt al de basis: geheime bestanden in plaats van wachtwoorden in platte tekst, poortbinding op localhost en gebruikersmapping. Hier volgen aanvullende beveiligingsmaatregelen.
Onnodige capabilities verwijderen
Voeg beveiligingsopties toe aan de webserver-service:
webserver:
# ... bestaande configuratie ...
security_opt:
- no-new-privileges:true
Dit voorkomt dat het containerproces extra privileges verkrijgt via setuid-binaries.
Resourcelimieten instellen
Voorkom dat een op hol geslagen OCR-proces alle serverresources verbruikt:
webserver:
# ... bestaande configuratie ...
deploy:
resources:
limits:
memory: 2g
cpus: "2.0"
reservations:
memory: 512m
Voor meer over resourcelimieten, zie onze handleiding over Docker Compose-resourcelimieten.
Versie-informatie verbergen
Configureer je reverse proxy om de Server-header te verwijderen. Met Nginx:
server_tokens off;
Paperless-ngx zelf toont geen versie in HTTP-headers, maar je reverse proxy misschien wel.
Hoe werk ik Paperless-ngx bij?
Pull de nieuwste images en maak de containers opnieuw aan. De Paperless-ngx-image voert databasemigraties automatisch uit bij het starten.
cd /opt/paperless-ngx
docker compose pull
docker compose up -d
Controleer na de update de logs op migratie-uitvoer:
docker compose logs webserver --tail 30
Zoek naar regels als Applying documents.XXXX_migration_name... OK. Als migraties mislukken, stopt de container. Raadpleeg de release notes voordat je grote versie-upgrades uitvoert.
Voer altijd je backupscript uit vóór het updaten.
Gaat er iets mis?
Container blijft unhealthy: Bekijk de logs met docker compose logs <service> --tail 50. Veelvoorkomende oorzaken: PostgreSQL-wachtwoord komt niet overeen (genereer .db_password opnieuw en maak het databasevolume opnieuw aan), Redis-verbinding geweigerd (broker nog niet gestart).
OCR produceert onleesbare tekst: Verkeerde taal ingesteld. Controleer of PAPERLESS_OCR_LANGUAGE overeenkomt met je documenten. Voor meertalige documenten voeg je alle relevante taalcodes toe, gescheiden door +.
Documenten verschijnen niet na upload: Controleer het consumer-log:
docker compose logs webserver --tail 50 | grep -i consumer
Veelvoorkomende oorzaak: rechtenprobleem. De consume-directory moet beschrijfbaar zijn door UID 1000 (of de waarde van USERMAP_UID):
chown -R 1000:1000 /opt/paperless-ngx/consume
Automatische tagging werkt niet: De classifier heeft minstens ~50 handmatig getagde documenten nodig. Controleer of de classifier is getraind:
docker compose exec webserver document_create_classifier
Schijfruimte groeit snel: Controleer welke documenten het grootst zijn:
docker compose exec webserver document_sanity_checker
Controleer ook je OCR-modus. De force-modus maakt aanzienlijk grotere archiefbestanden dan skip.
E-mailverwerking haalt geen mail op: Controleer de IMAP-inloggegevens in de webinterface. Activeer handmatig ophalen om fouten te zien:
docker compose exec webserver mail_fetcher
Voor meer zelfhosting-handleidingen, zie onze Immich-handleiding voor fotobeheer op dezelfde stack.
Copyright 2026 Virtua.Cloud. Alle rechten voorbehouden. Deze inhoud is een origineel werk van het Virtua.Cloud-team. Reproductie, herpublicatie of herdistributie zonder schriftelijke toestemming is verboden.
Klaar om het zelf te proberen?
Deploy uw eigen server in seconden. Linux, Windows of FreeBSD.
Bekijk VPS-aanbod