SSD Nodes Learn Hosting plans →
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-28

Paperless-ngx auf einem VPS mit Docker Compose einrichten

Installieren Sie paperless-ngx auf einem VPS mit Docker Compose: offizieller Postgres-Stack, PAPERLESS_URL, Consume-Ordner, OCR-Sprachen, HTTPS und Backups.

Was Sie erstellen

Paperless-ngx verwandelt einen Ordner mit gescannten Dokumenten auf einem VPS in ein durchsuchbares Archiv. Sie legen eine PDF-Datei in einem überwachten Verzeichnis ab. Der Server führt darauf OCR (optische Zeichenerkennung) aus, extrahiert den Text, ermittelt das Datum und den Korrespondenten und legt die Datei ab. Die Installation besteht aus einer Docker-Compose-Datei mit vier Diensten. Danach geht es nur noch um die Konfiguration. Dieser Leitfaden widmet ihr deshalb den größten Teil, weil Installationen an dieser Stelle häufig fehlschlagen. Paperless-ngx ist keine Fotobibliothek: OCR und die Ermittlung von Korrespondenten helfen bei einem Ordner mit Urlaubs-JPEG-Dateien nicht weiter. Legen Sie diese Dateien stattdessen in einem dafür geeigneten Fotoserver ab und verwenden Sie Paperless für Dokumente. Für Videos gilt dieselbe Abgrenzung: Eine Sammlung gerippter Filme gehört auf einen Medienserver. Dort macht beispielsweise eine Jellyfin-Oberfläche im Stil einer Videothek der 90er-Jahre das Stöbern zum eigentlichen Zweck und nicht die Suche.

Paperless-ngx ist der von der Community gepflegte Fork des ursprünglichen Paperless-Projekts. Die Software ist kostenlos, lässt sich selbst hosten und speichert Ihre Dokumente als normale Dateien auf dem Datenträger. Dadurch verlieren Sie nie den Zugriff auf Ihr eigenes Archiv. Wenn Sie die Software auf einem VPS statt auf einem Rechner zu Hause betreiben, sind Ihre Scans von überall erreichbar, ohne einen Port auf Ihrem Heimrouter öffnen zu müssen. Das passt gut zu einer privaten Nextcloud-Instanz für Dateien, die nicht auf Papier vorliegen. Dasselbe gilt für den Desktop, an den Ihr Scanner angeschlossen ist. Mit einem eigenen RustDesk-Relay auf diesem VPS können Sie diesen Rechner von einem anderen Ort aus bedienen, ohne ebenfalls eine Lücke in der Router-Firewall öffnen zu müssen.

Was der Stack tatsächlich ausführt

Die offizielle Compose-Datei startet vier Container. Wenn Sie die Funktion jedes Containers kennen, lassen sich die Logs leichter lesen.

  • webserver: das paperless-ngx-Image selbst. Es stellt die Weboberfläche und die API bereit, überwacht den Eingangsordner und führt die Celery-Task-Worker für die OCR-Verarbeitung aus.
  • 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 Key-Value-Speicher. Er bildet die Task-Warteschlange zwischen dem Webprozess und den Workern.
  • gotenberg und tika: optional und nur in den -tika-Compose-Varianten enthalten. Sie konvertieren Office-Dokumente (.docx, .xlsx, .odt) in PDF, damit paperless sie indizieren kann.

Stand Juli 2026 verwendet die PostgreSQL-Compose-Datei fest docker.io/library/postgres:18 und docker.io/valkey/valkey:9-alpine und ruft die Anwendung aus ghcr.io/paperless-ngx/paperless-ngx:latest ab.

Voraussetzungen

  • Ein Ubuntu 24.04 KVM-VPS mit sudo-Zugriff sowie bereits installiertem Docker und Compose-Plugin. Wenn 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 zeigt. Paperless verweigert die Bereitstellung unter einem Hostnamen, der nicht konfiguriert wurde. Das ist daher früher relevant, als Sie vielleicht erwarten.
  • Der Arbeitsspeicher ist die entscheidende Einschränkung. PostgreSQL, Valkey, gunicorn und ein Tesseract-OCR-Worker können für eine geringe Nutzung gleichzeitig in 2 GB ausgeführt werden. Verwenden Sie 4 GB, wenn Sie einen Rückstand von mehreren hundert Scans importieren möchten. Die OCR-Verarbeitung eines großen mehrseitigen PDF ist die Speicherbelastung, bei der der Kernel einen Worker wegen Speichermangels beendet.
  • Speicherplatz: Ihr Archiv wird zweimal gespeichert: als Originaldatei und als OCR-verarbeitetes 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. Manuell sind es vier Befehle. Dabei wissen Sie genau, wo alles liegt. Das ist auf einem Server wichtig, den Sie selbst warten.

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 liegen 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 jedoch deutlich früher langsam als bei 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 die Datei daher nicht. Andernfalls kann docker compose down -v Ihre Daten nicht mehr finden.

docker-compose.env vor dem ersten Start konfigurieren

Zwei Einstellungen sind erforderlich. Generieren 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 standardmäßig mit dem Literalwert change-me ausgeliefert. Damit werden Session-Cookies signiert. Wenn Sie den Standardwert beibehalten, kann jeder, der ihn kennt, eine Session fälschen. Setzen Sie den Wert 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, wird DisallowedHost im Container-Log angezeigt. Tragen Sie den Wert ohne abschließenden Schrägstrich und ohne Pfad ein.

USERMAP_UID und USERMAP_GID legen den Benutzer fest, unter dem der Container ausgeführt wird. Stimmen Sie die Werte mit Ihrem eigenen Konto ab. Prüfen Sie dieses 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 erscheint dann ein Berechtigungsfehler statt eines Imports.

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 fordert Sie zur Eingabe eines Benutzernamens, einer E-Mail-Adresse und eines Passworts auf. Es gibt keine Standardanmeldung. Wenn Sie diesen Schritt überspringen, sehen Sie eine Anmeldeseite, die keine Anmeldung akzeptiert. Warten Sie die Logzeile ab, die meldet, dass der Server auf Port 8000 lauscht, bevor Sie den Zugriff über den Browser testen. 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 302-Weiterleitung zu /accounts/login/ bedeutet, dass der Stack ordnungsgemäß läuft.

HTTPS davor schalten

Die Standard-Compose-Datei veröffentlicht 8000:8000 und bindet damit an alle Schnittstellen. Auf einem öffentlichen VPS wird dadurch Ihr gesamtes Dokumentarchiv unverschlüsselt per HTTP für jeden erreichbar, der die Adresse findet. Ändern Sie die Portzeile so, dass sie nur an die Loopback-Schnittstelle bindet:

    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 Zertifikatskonfiguration laufen, folgen Sie dem Traefik-Reverse-Proxy-Muster für mehrere Docker-Compose-Anwendungen und verbinden Sie den Dienst webserver ohne veröffentlichten Port mit dem Proxy-Netzwerk.

Unabhängig vom verwendeten Proxy muss dieser X-Forwarded-Proto: https senden. Ohne diesen Header geht Django davon aus, dass die Anfrage über HTTP eingegangen ist. Die Origin-Prüfung des Anmeldeformulars schlägt dann fehl, und auf einer ansonsten korrekt aussehenden Seite wird CSRF verification failed. Request aborted. angezeigt. Die andere Hälfte der Korrektur besteht darin, PAPERLESS_URL auf exakt die Adresse https:// zu setzen, die Sie im Browser eingeben.

Erhöhen Sie außerdem das Upload-Limit des Proxys. Ein 40 MB großer Scan wird von einem Proxy mit einer Begrenzung auf 1 MB abgewiesen, bevor paperless ihn überhaupt erreicht. Der Browser meldet dann einen allgemeinen Upload-Fehler.

So funktioniert das Consume-Verzeichnis

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, weil die Datei nun im von paperless verwalteten Medien-Volume liegt.

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

Sie sollten sehen, wie der Consumer den Dateinamen übernimmt, die OCR-Verarbeitung ausführt und mit einer Meldung abschließt, dass das Dokument hinzugefügt wurde. Der gesamte Vorgang 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 weist paperless an, auch in Unterverzeichnissen zu suchen. PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true macht aus jedem Unterverzeichnisnamen ein Tag. Wenn Sie eine Datei in consume/invoices/2026/ ablegen, erhält sie die Tags invoices und 2026. Das ist das einfachste Ablagesystem, das Sie jemals einrichten werden.

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 schreibt, wird keine Datei erkannt. Setzen Sie in diesem Fall das Intervall auf eine positive Anzahl von Sekunden, damit paperless das Verzeichnis stattdessen regelmäßig durchsucht.

OCR-Sprachen und ihre Kosten

PAPERLESS_OCR_LANGUAGE erwartet einen dreistelligen Tesseract-Code. Standardmäßig ist eng eingestellt. Kombinieren Sie Sprachen mit einem Pluszeichen, zum Beispiel deu+eng. Tesseract versucht dann jede Sprache und behält das beste Ergebnis. Jede zusätzliche Sprache vervielfacht dadurch die für jede Seite benötigte CPU-Zeit. Auf einer VPS mit gemeinsam genutzter vCPU kann das den Unterschied zwischen einem Scan ausmachen, der nach zehn Sekunden fertig ist, und einem Scan, der eine Minute benötigt. 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 fügen Sie diese als durch Leerzeichen getrennte Liste zu PAPERLESS_OCR_LANGUAGES hinzu, 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

Das Kopieren der Docker-Volumes während PostgreSQL läuft, erzeugt möglicherweise ein Backup, das sich nicht wiederherstellen lässt. Paperless enthält einen eigenen Exporter. Er schreibt die Dokumente sowie ein JSON-Manifest mit allen Metadaten in den ./export-Bind-Mount:

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

--delete entfernt exportierte Dateien, die keinem aktuellen Dokument mehr entsprechen. Dadurch bleibt das Verzeichnis ein Abbild, statt dauerhaft zu wachsen. --no-progress-bar hält die Ausgabe sauber, wenn der Export über cron ausgeführt wird.

Die Wiederherstellung erfolgt document_importer aus demselben Verzeichnis in einem frisch gestarteten Stack. Dadurch müssen Sie nur das Exportverzeichnis sicher aufbewahren. Übertragen Sie es regelmäßig an einen anderen Standort, beispielsweise mit verschlüsselten, deduplizierten restic-Backups von Ihrem VPS, und führen Sie den Export zuerst aus. So erfasst restic niemals ein unvollständig geschriebenes Archiv.

Überprüfen Sie ein Backup, indem Sie kontrollieren, dass export/manifest.json vorhanden ist und die Dateianzahl mit der Dokumentanzahl in der Oberfläche übereinstimmt. Ein Backup, dessen Inhalt Sie nie aufgelistet haben, ist kein Backup. Ein nächtlicher Export, der unbemerkt fehlschlägt, ist noch problematischer. Lassen Sie den Cronjob seinen Exit-Status an Ihren eigenen ntfy-Server senden. So erfahren Sie in der Woche, in der der Export fehlschlägt, davon und nicht erst an dem Tag, an dem Sie das Backup wiederherstellen müssen.

FAQ

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

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 Bearbeiten der Env-Datei allein bewirkt nichts, weil der laufende Container die Umgebung beibehält, 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 die Werte und erstellen Sie den Container neu. Gibt es überhaupt keinen Log-Eintrag, ist das Dateiereignis nicht angekommen. Das passiert bei Netzwerkfreigaben, weil Kernel-Benachrichtigungen diese nicht überschreiten. Setzen Sie PAPERLESS_CONSUMER_POLLING_INTERVAL beispielsweise auf 30. Dann scannt paperless den Ordner stattdessen alle 30 Sekunden.

Kann ich paperless-ngx anstelle von PostgreSQL mit SQLite betreiben?

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 das Archiv wächst: Die Volltextsuche und die Bearbeitung von Tags für viele Dokumente werden bei mehreren tausend Dokumenten deutlich 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 der doppelten Größe Ihrer Quelldateien. Paperless bewahrt das Original unverändert auf und speichert zusätzlich eine zweite PDF-Datei mit OCR und durchsuchbarer Textebene sowie kleine Vorschaubilder. Ein reiner Textscan mit 200 KB bleibt klein. Ein 30 MB großer Farbscan eines langen Vertrags belegt etwa 60 MB. Wenn Sie das Exportverzeichnis auf derselben Festplatte aufbewahren, müssen Sie es zusätzlich einrechnen. Dasselbe Archiv belegt dann auf dem Datenträger etwa das Dreifache.

Benötige ich die Container Tika und Gotenberg?

Nur wenn Word-, Excel- oder OpenDocument-Dateien zusammen mit Ihren PDFs indiziert werden sollen. Die Container konvertieren diese Formate in PDF, damit paperless sie per OCR verarbeiten und durchsuchen kann. Sie fügen außerdem zwei weitere laufende Container und einige hundert Megabyte Arbeitsspeicher hinzu. Lassen Sie sie daher auf einem kleinen System weg, wenn alle abgelegten Dateien bereits PDFs oder Bilder sind.

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