SSD Nodes Learn 8GB RAM — $66/Jahr
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-01

Paperless-ngx auf einem VPS mit Docker Compose einrichten

Installieren Sie Paperless-ngx mit dem offiziellen Postgres-Stack, konfigurieren Sie PAPERLESS_URL, Consume-Ordner und OCR-Sprachen, sichern Sie HTTPS und Backups ab.

Was Sie erstellen

Paperless-ngx auf einem VPS wandelt einen Ordner mit gescannten Dokumenten in ein durchsuchbares Archiv um. Sie legen eine PDF-Datei in ein überwachtetes Verzeichnis. Der Server führt darauf OCR (optische Zeichenerkennung) aus, extrahiert den Text, ermittelt ein Datum und einen Korrespondenten und legt die Datei ab. Die Installation besteht aus einer Docker-Compose-Datei mit vier Services. Danach geht es nur noch um die Konfiguration. Dieser Leitfaden verwendet dafür den größten Teil seines Umfangs, weil Installationen an dieser Stelle häufig fehlschlagen.

Paperless-ngx ist der gepflegte Community-Fork des ursprünglichen Paperless-Projekts. Die Software ist kostenlos, kann selbst gehostet werden und speichert Ihre Dokumente als normale Dateien auf dem Datenträger. Dadurch bleibt Ihr eigenes Archiv jederzeit zugänglich. Wenn Sie die Software auf einem VPS statt auf einem Rechner zu Hause ausführen, können Sie von überall auf Ihre Scans zugreifen, ohne einen Port am Router zu Hause zu öffnen. Außerdem lässt sie sich gut mit einer privaten Nextcloud-Instanz für Dateien verbinden, die nicht auf Papier vorliegen.

Was der Stack tatsächlich ausführt

Die offizielle Compose-Datei startet vier Container. Wenn Sie die Aufgabe jedes Containers kennen, können Sie die Logs besser lesen.

  • webserver: das paperless-ngx-Image selbst. Es stellt die Weboberfläche und die API bereit. Außerdem führt es den Consumer aus, der Ihren Eingabeordner überwacht, sowie die Celery-Task-Worker, die die OCR-Verarbeitung übernehmen.
  • db: PostgreSQL. Es speichert Metadaten, Tags, Korrespondenten und die Tabellen für den Volltextsuchindex. Ihre PDFs werden dort nicht gespeichert.
  • broker: Valkey, ein Redis-kompatibler Schlüsselwertspeicher. Er dient als Aufgabenwarteschlange zwischen dem Webprozess und den Workern.
  • gotenberg und tika: optional, nur in den -tika-Compose-Varianten. Sie konvertieren Office-Dokumente (.docx, .xlsx, .odt) in PDF, damit paperless sie indizieren kann.

Stand Juli 2026 legt die Postgres-Compose-Datei docker.io/library/postgres:18 und docker.io/valkey/valkey:9-alpine fest und lädt die Anwendung aus ghcr.io/paperless-ngx/paperless-ngx:latest.

Voraussetzungen

  • Ein Ubuntu 24.04 KVM-VPS mit sudo-Zugriff sowie Docker mit bereits installiertem Compose-Plugin. Falls dieser Teil neu für Sie ist, beginnen Sie mit den Grundlagen von Docker Compose für einen VPS und kehren Sie anschließend hierher zurück.
  • Ein Domainname mit einem A-Record, der auf den VPS verweist. Paperless lehnt die Bereitstellung unter einem Hostnamen ab, der nicht konfiguriert wurde. Das ist daher früher relevant, als Sie vielleicht erwarten.
  • Der Arbeitsspeicher ist die eigentliche Einschränkung. PostgreSQL, Valkey, gunicorn und ein Tesseract-OCR-Worker können bei geringer Nutzung gleichzeitig in 2 GB betrieben werden. Weisen Sie 4 GB zu, wenn Sie einen Rückstand von mehreren hundert Scans importieren möchten. Die OCR-Verarbeitung eines großen mehrseitigen PDF ist der Speicherbedarfssprung, durch den der Kernel einen Worker wegen Speichermangels beendet.
  • Speicherplatz: Ihr Archiv wird zweimal gespeichert, als Originaldatei und als OCR-Archiv-PDF. Planen Sie daher ungefähr die doppelte Größe Ihrer Scans ein.

Offizielle Compose-Dateien abrufen

Es gibt einen interaktiven Installer:

bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"

Er stellt Fragen und schreibt die Dateien für Sie. Wenn Sie dies manuell durchführen, benötigen Sie vier Befehle und wissen anschließend, wo sich alles befindet. Das ist für einen Server, den Sie verwalten, die bessere Vorgehensweise.

mkdir -p ~/paperless && cd ~/paperless
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.postgres.yml
curl -fsSL -o docker-compose.env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.env
curl -fsSL -o .env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/.env

Die Varianten befinden sich im selben Verzeichnis: docker-compose.sqlite.yml, docker-compose.mariadb.yml und jeweils eine -tika-Version. Wählen Sie für eine neue Installation postgres. SQLite eignet sich für einige hundert Dokumente. Der Volltextsuchindex wird damit jedoch deutlich früher langsam als mit PostgreSQL.

Die Datei .env enthält eine Zeile: COMPOSE_PROJECT_NAME=paperless. Dieser Name wird zum Präfix für jeden Container und jedes Volume. Löschen Sie ihn daher nicht, um sich anschließend zu fragen, warum docker compose down -v Ihre Daten nicht findet.

docker-compose.env vor dem ersten Start konfigurieren

Zwei Einstellungen sind erforderlich. Erzeugen Sie den geheimen Schlüssel mit dem vom Projekt dokumentierten Befehl:

python3 -c "import secrets; print(secrets.token_urlsafe(64))"

Bearbeiten Sie anschließend docker-compose.env:

PAPERLESS_SECRET_KEY=<the long string you just generated>
PAPERLESS_URL=https://paperless.example.com
PAPERLESS_TIME_ZONE=Europe/Berlin
PAPERLESS_OCR_LANGUAGE=deu+eng
USERMAP_UID=1000
USERMAP_GID=1000

PAPERLESS_SECRET_KEY wird mit dem Literalwert change-me ausgeliefert. Damit werden Sitzungscookies signiert. Wenn Sie den Wert unverändert lassen, kann jeder, der den Standardwert kennt, eine Sitzung fälschen. Setzen Sie ihn vor dem ersten Start, da eine spätere Änderung alle Benutzer abmeldet.

PAPERLESS_URL erspart Ihnen eine Stunde Arbeit. Paperless ist eine Django-Anwendung, und Django validiert den Host-Header jeder Anfrage. Setzen Sie PAPERLESS_URL, dann werden ALLOWED_HOSTS, CORS_ALLOWED_HOSTS und CSRF_TRUSTED_ORIGINS automatisch ausgefüllt. Wenn der Wert leer bleibt, Sie eine Domain auf den Server zeigen lassen und jede Seite Bad Request (400) zurückgibt, finden Sie DisallowedHost im Container-Log. Geben Sie den Wert ohne abschließenden Schrägstrich und ohne Pfad an.

USERMAP_UID und USERMAP_GID legen den Benutzer fest, unter dem der Container ausgeführt wird. Stimmen Sie diese Werte mit Ihrem eigenen Konto ab. Prüfen Sie es mit id -u und id -g. Stimmen die Werte nicht überein, können die Dateien, die Sie in den consume-Ordner kopieren, vom Consumer nicht gelesen werden. Im Log wird dann ein Berechtigungsfehler statt eines Imports angezeigt.

Stack starten und den ersten Benutzer anlegen

docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webserver

createsuperuser fragt nach einem Benutzernamen, einer E-Mail-Adresse und einem Passwort. Es gibt keine Standardanmeldung. Wenn Sie diesen Schritt überspringen, gelangen Sie zu einer Anmeldeseite, die keine Anmeldung akzeptiert. Warten Sie auf die Logzeile, die meldet, dass der Server an Port 8000 lauscht, bevor Sie den Browser verwenden. Beim allerersten Start werden außerdem Datenbankmigrationen ausgeführt. Das dauert ein bis zwei Minuten.

Prüfen Sie die Anwendung lokal, bevor Sie eine Domain verwenden:

curl -I http://127.0.0.1:8000

Eine Weiterleitung von 302 zu /accounts/login/ bedeutet, dass der Stack ordnungsgemäß funktioniert.

HTTPS davor schalten

Die standardmäßige Compose-Datei veröffentlicht 8000:8000 und bindet damit an jede Schnittstelle. Auf einem öffentlichen VPS stellt sie Ihr gesamtes Dokumentarchiv über unverschlüsseltes HTTP für jeden bereit, der die Adresse findet. Ändern Sie die Portzeile so, dass sie nur an die Loopback-Schnittstelle gebunden wird:

    ports:
      - "127.0.0.1:8000:8000"

Beenden Sie TLS (Transport Layer Security) anschließend in einem Reverse-Proxy und leiten Sie die Anfragen an 127.0.0.1:8000 weiter. Wenn dies die einzige Anwendung auf dem Server ist, reicht jeder Proxy mit einem ACME-Client (Automatic Certificate Management Environment) aus. Wenn mehrere Container hinter einer gemeinsamen Zertifikatkonfiguration ausgeführt werden, folgen Sie dem Muster für einen Traefik-Reverse-Proxy mit mehreren Docker-Compose-Anwendungen und verbinden Sie den Dienst webserver mit dem Proxy-Netzwerk, ohne einen Port zu veröffentlichen.

Unabhängig vom verwendeten Proxy muss er X-Forwarded-Proto: https senden. Ohne diese Einstellung geht Django davon aus, dass die Anfrage über HTTP eingegangen ist. Die Origin-Prüfung des Anmeldeformulars schlägt fehl, und auf einer ansonsten korrekt aussehenden Seite wird CSRF verification failed. Request aborted. angezeigt. Die zweite erforderliche Einstellung ist PAPERLESS_URL mit genau der https://-Adresse, die Sie im Browser eingeben.

Erhöhen Sie außerdem das Uploadgrößenlimit des Proxys. Ein 40-MB-Scan wird von einem Proxy, der Anfragerümpfe auf 1 MB begrenzt, abgewiesen, bevor paperless ihn überhaupt erhält. Der Browser meldet dann einen allgemeinen Uploadfehler.

Funktionsweise des Consume-Verzeichnisses

Die Compose-Datei bind-mountet ./consume aus dem Compose-Verzeichnis in den Container. Alles, was Sie dort ablegen, wird importiert und anschließend aus dem Verzeichnis gelöscht, da die Datei nun unter der Verwaltung von paperless im Medien-Volume liegt.

cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserver

Der Consumer sollte den Dateinamen übernehmen, die OCR ausführen und mit einer Zeile abschließen, die meldet, dass das Dokument hinzugefügt wurde. Der gesamte Ablauf dauert bei einem einseitigen Scan wenige Sekunden. Bei einem langen Dokument kann er eine Minute oder länger dauern.

Zwei Einstellungen ändern, wie Dateien gefunden werden. PAPERLESS_CONSUMER_RECURSIVE=true veranlasst paperless, in Unterverzeichnissen zu suchen. PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true wandelt jeden Namen eines Unterverzeichnisses in ein Tag um. Wenn Sie eine Datei in consume/invoices/2026/ ablegen, erhält sie die Tags invoices und 2026. Das ist das einfachste Ablagesystem, das Sie einrichten können.

Die Erkennung ist die andere Hälfte. Standardmäßig ist PAPERLESS_CONSUMER_POLLING_INTERVAL auf 0 gesetzt. Das bedeutet, dass paperless Benachrichtigungen des Kernel-Dateisystems verwendet, die sofort ausgelöst werden. Diese Benachrichtigungen werden nicht über ein Netzwerkdateisystem übertragen. Wenn Ihr Consume-Verzeichnis eine NFS- oder SMB-Freigabe ist, in die ein Netzwerkscanner schreiben kann, wird nichts erkannt. Setzen Sie in diesem Fall das Intervall auf eine positive Anzahl von Sekunden, damit paperless das Verzeichnis stattdessen scannt.

OCR-Sprachen und ihre Kosten

PAPERLESS_OCR_LANGUAGE erwartet standardmäßig den dreistelligen Tesseract-Code eng. Sie können Sprachen mit einem Pluszeichen kombinieren, zum Beispiel deu+eng. Tesseract versucht dann jede Sprache und behält das beste Ergebnis. Jede zusätzliche Sprache vervielfacht dadurch die CPU-Zeit für jede Seite. Auf einem VPS mit gemeinsam genutzten vCPUs entscheidet das darüber, ob ein Scan in zehn Sekunden oder erst nach einer Minute abgeschlossen ist. Geben Sie nur die Sprachen an, in denen Ihre Dokumente tatsächlich verfasst sind.

Das Image enthält Englisch, Deutsch, Italienisch, Spanisch und Französisch. Für jede weitere Sprache ergänzen Sie sie als durch Leerzeichen getrennte Liste in PAPERLESS_OCR_LANGUAGES, zum Beispiel PAPERLESS_OCR_LANGUAGES=tur ces, und starten Sie den Container neu. Der Container lädt die Tesseract-Datenpakete beim Start herunter. Der erste Start nach dieser Änderung dauert daher länger.

Datenbank und Medien sichern

Wenn Sie die Docker-Volumes bei laufendem PostgreSQL kopieren, erhalten Sie möglicherweise ein Backup, das sich nicht wiederherstellen lässt. Paperless enthält einen eigenen Exporter. Dieser schreibt die Dokumente sowie ein JSON-Manifest aller Metadaten in den ./export-Bind-Mount:

docker compose exec webserver document_exporter ../export --delete --no-progress-bar

--delete entfernt exportierte Dateien, die nicht mehr zu einem aktuellen Dokument passen. Dadurch bleibt der Ordner ein Spiegel der aktuellen Dokumente, statt dauerhaft zu wachsen. --no-progress-bar hält die Ausgabe sauber, wenn der Export über cron ausgeführt wird.

Die Wiederherstellung erfolgt mit document_importer aus demselben Ordner in einem frischen Stack. Sie müssen daher nur das Exportverzeichnis sicher aufbewahren. Senden Sie es regelmäßig an einen externen Speicherort. Verwenden Sie dazu verschlüsselte, deduplizierte restic-Backups von Ihrem VPS. Führen Sie den Export zuerst aus, damit restic niemals ein unvollständig geschriebenes Archiv erfasst.

Überprüfen Sie ein Backup, indem Sie kontrollieren, dass export/manifest.json vorhanden ist und die Dateianzahl mit der Dokumentanzahl in der Benutzeroberfläche übereinstimmt. Ein Backup, das Sie noch nie aufgelistet haben, ist kein Backup.

FAQ

Warum gibt jede Seite "Bad Request (400)" zurück, nachdem ich meine Domain darauf verweise?

Django hat den Host-Header abgelehnt, weil Ihre Domain nicht in ALLOWED_HOSTS enthalten ist. Setzen Sie PAPERLESS_URL=https://paperless.example.com in docker-compose.env, ohne abschließenden Schrägstrich, und führen Sie anschließend docker compose up -d aus, um den Container neu zu erstellen. Das alleinige Bearbeiten der env-Datei bewirkt nichts, weil der laufende Container die Umgebung verwendet, mit der er gestartet wurde.

Ich habe eine PDF-Datei im consume-Ordner abgelegt, aber es ist nichts passiert. Was ist falsch?

Prüfen Sie zuerst docker compose logs webserver. Ein Berechtigungsfehler bedeutet, dass USERMAP_UID und USERMAP_GID nicht mit dem Konto übereinstimmen, dem die Datei gehört. Korrigieren Sie diese Werte und erstellen Sie den Container neu. Wenn überhaupt keine Logzeile erscheint, ist das Dateiereignis nicht eingegangen. Das passiert bei Netzwerkfreigaben, weil Kernel-Benachrichtigungen diese nicht überqueren. Setzen Sie PAPERLESS_CONSUMER_POLLING_INTERVAL beispielsweise auf 30. Dann scannt paperless den Ordner stattdessen alle 30 Sekunden.

Kann ich paperless-ngx mit SQLite statt PostgreSQL ausführen?

Ja, docker-compose.sqlite.yml wird unterstützt und benötigt weniger Arbeitsspeicher. Das eignet sich für einen kleinen VPS. Der Nachteil zeigt sich, wenn Ihr Archiv wächst: Die Volltextsuche und umfangreiche Änderungen an Tags werden bei mehreren tausend Dokumenten spürbar langsamer. Eine spätere Migration erfordert einen Export und einen Import. Wählen Sie daher jetzt PostgreSQL, wenn das Archiv voraussichtlich weiter wächst.

Wie viel Speicherplatz benötigt ein Archiv mit Scans tatsächlich?

Rechnen Sie grob mit dem Doppelten der Größe Ihrer Quelldateien. Paperless bewahrt das Original unverändert auf und speichert zusätzlich eine zweite PDF-Datei mit OCR und einer durchsuchbaren Textebene sowie kleine Vorschaubilder. Ein textbasierter Scan mit 200 KB bleibt klein. Ein 30 MB großer Farbscan eines langen Vertrags benötigt etwa 60 MB. Wenn Sie das Exportverzeichnis auf derselben Festplatte speichern, müssen Sie dieses hinzurechnen. Dann belegt dasselbe Archiv auf der Festplatte dreimal so viel Speicherplatz.

Benötige ich die Container Tika und Gotenberg?

Nur wenn Sie Word-, Excel- oder OpenDocument-Dateien zusammen mit Ihren PDFs indizieren möchten. Diese Formate werden in PDF konvertiert, damit paperless sie per OCR verarbeiten und durchsuchen kann. Außerdem kommen zwei weitere laufende Container und einige hundert Megabytes Arbeitsspeicher hinzu. Lassen Sie sie auf einem kleinen System weg, wenn alle Dateien, die Sie archivieren, bereits PDFs oder Bilder sind.

#paperless-ngx#documents#self-hosting#docker#ocr