Der Ordner im Regal, die Kiste mit Briefen, der Schuhkarton mit Kassenbons für die Steuer: Papier hat die Angewohnheit, genau dann zu verschwinden, wenn man es braucht. Paperless-ngx ist die Antwort der Selbsthoster darauf. Du scannst oder fotografierst ein Dokument, wirfst es in einen Ordner, und kurz darauf ist es durchsuchbar, mit Absender, Datum und Schlagwörtern versehen und liegt auf deiner eigenen Platte.
Das „ngx“ im Namen steht für die Fortführung durch die Community: Das ursprüngliche Paperless wurde nicht mehr gepflegt, Freiwillige haben es übernommen und seitdem weitergebaut. Open Source, sehr aktiv, und die Dokumente bleiben ganz normale PDFs in einem Ordner. Willst du das Programm irgendwann nicht mehr, nimmst du die Dateien einfach mit. Das ist für mich der Grund, Rechnungen lieber hier zu lagern als bei einem Cloud-Dienst, der mir den Speicher vermietet und den Export erschwert.

Was Paperless-ngx mit deinem Scan macht
Bevor du tippst, einmal der Weg eines Dokuments. Du legst eine Datei in den Eingangsordner. Paperless holt sie sich, erkennt den Text, erzeugt eine archivtaugliche PDF-Kopie und verschiebt beides in seinen Speicher. Danach ist die Datei im Eingangsordner weg. Das erschreckt beim ersten Mal, ist aber gewollt: Der Ordner ist der Briefschlitz, nicht das Archiv.
Der Text wird von Tesseract erkannt, einer freien Texterkennung, die im Container schon steckt. Deutsch, Englisch, Französisch, Italienisch und Spanisch sind laut Doku vorinstalliert. Die Qualität hängt am Scan: Ein gerader, gut belichteter Scan mit 300 dpi liefert brauchbaren Text, ein schräges Handyfoto im Halbdunkel eher Buchstabensalat.
Was du brauchst
Einen Linux-Rechner, auf dem Docker samt Compose-Plugin läuft. Das kann ein alter Büro-PC sein, ein NUC, eine VM auf Proxmox oder ein Raspberry Pi. Zur Hardware sagt die Doku wenig Konkretes; sie hat aber einen eigenen Abschnitt für schwache Geräte, also läuft es auch dort, nur langsamer bei der Texterkennung. Prüf zuerst, ob Docker da ist:
docker --version
docker compose versionBeide Befehle müssen eine Versionsnummer ausgeben. Meldet der zweite „docker: 'compose' is not a docker command“, fehlt das Compose-Plugin, und du installierst es nach der Docker-Doku nach, bevor es weitergeht.
Dann den Port. Paperless lauscht standardmäßig auf 8000, und der ist auf Bastelrechnern gern schon vergeben. Der Befehl darf nichts ausgeben:
ss -tulpn | grep :8000Kommt doch eine Zeile, sitzt dort schon ein Dienst. Wer das ist und wie du ihn wegbekommst, steht in Port belegt? So findest du den Schuldigen. Oder du gibst Paperless in Schritt 2 einfach einen anderen Port; das ist eine Zeile.
Zuletzt deine Benutzer-ID. Paperless läuft im Container als eigener Benutzer und muss Dateien in deinem Eingangsordner löschen dürfen. Dafür bekommt es gleich dieselbe Nummer wie du:
id -u
id -gBei den meisten heißt die Antwort zweimal 1000. Merk dir beide Zahlen, sie kommen in Schritt 2 in die Konfiguration.
- Server im Heimnetz:
192.168.178.20 - Ordner für Paperless:
/opt/paperless - Eingangsordner:
/opt/paperless/consume - Port im Browser:
8000 - Benutzer- und Gruppen-ID aus
id -u/id -g:1000/1000 - OCR-Sprache:
deu, Zeitzone:Europe/Berlin
Schritt 1: Ordner anlegen und die drei Dateien holen
Paperless liefert seinen Bauplan fertig mit: Im Projekt liegt ein Ordner mit Compose-Dateien für verschiedene Datenbanken. Du brauchst drei Dateien daraus, und du lädst sie unverändert herunter, statt sie abzutippen. Die Doku bietet auch ein Skript an, das dich alles der Reihe nach abfragt und die Dateien selbst schreibt. Ich gehe hier den Weg von Hand, weil du dann weißt, was in den drei Dateien steht. Spätestens beim ersten Fehler brauchst du das.
sudo mkdir -p /opt/paperless/consume /opt/paperless/export
sudo chown -R "$USER":"$USER" /opt/paperless
cd /opt/paperless
curl -fsSLo docker-compose.yml https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.sqlite.yml
curl -fsSLo docker-compose.env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.env
curl -fsSLo .env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/.env
ls -laDie beiden Unterordner legst du selbst an, damit sie dir gehören und nicht dem Root-Benutzer, den Docker sonst nimmt. Genau daran scheitert später das Löschen im Eingangsordner. Die Ausgabe von ls -la zeigt am Ende drei Dateien und zwei Ordner:
drwxr-xr-x consume
-rw-r--r-- docker-compose.env
-rw-r--r-- docker-compose.yml
-rw-r--r-- .env
drwxr-xr-x exportZur Wahl der Datenbank: Ich habe die SQLite-Variante genommen. Sie braucht keinen extra Datenbank-Container, die Datenbank ist eine einzelne Datei, und die Doku empfiehlt sie ausdrücklich für schwächere Geräte. PostgreSQL rät die Doku, wenn mehrere Leute gleichzeitig arbeiten. Für einen Haushalt mit einem Scanner reicht SQLite; willst du trotzdem Postgres, nimmst du docker-compose.postgres.yml statt der sqlite-Datei, der Rest der Anleitung bleibt gleich.
Was in der Compose-Datei steht, kannst du mit cat docker-compose.yml nachlesen: zwei Dienste, der Webserver mit Paperless selbst und ein Broker namens Valkey, der Aufträge wie „diese Datei einlesen“ in die Warteschlange stellt. Valkey ist der offene Fork von Redis nach dessen Lizenzwechsel 2024; du musst nichts damit tun. Die Datei .env enthält nur eine Zeile mit dem Projektnamen paperless, nach dem Docker die Speicher-Volumes benennt.
Schritt 2: docker-compose.env anpassen
Alle Einstellungen für Paperless stehen in docker-compose.env. Die heruntergeladene Version ist fast komplett auskommentiert, das Raute-Zeichen am Zeilenanfang schaltet eine Zeile ab. Du brauchst vier Dinge: deine Benutzer-ID, einen eigenen geheimen Schlüssel, die Zeitzone und die OCR-Sprache. Den Schlüssel erzeugst du zuerst, den Befehl dafür verrät die Datei selbst in einem Kommentar:
python3 -c "import secrets; print(secrets.token_urlsafe(64))"Die lange Zeichenkette kopierst du gleich in die Datei. Paperless unterschreibt damit Anmelde-Sitzungen. Der mitgelieferte Platzhalter change-me steht in jedem Tutorial der Welt, also könnte jeder mit dem Wert eine Sitzung fälschen, sobald Paperless mal von außen erreichbar ist. Merken musst du dir den Schlüssel nicht, nur nie mehr ändern, sonst fliegen alle aus ihrer Anmeldung.
nano docker-compose.envIm Editor entfernst du bei diesen Zeilen die Raute am Anfang und trägst deine Werte ein. Am Ende sehen die aktiven Zeilen so aus (bei PAPERLESS_SECRET_KEY steht natürlich deine eigene Zeichenkette):
USERMAP_UID=1000
USERMAP_GID=1000
PAPERLESS_SECRET_KEY=hier-deine-lange-zeichenkette
PAPERLESS_TIME_ZONE=Europe/Berlin
PAPERLESS_OCR_LANGUAGE=deuSpeichern mit Strg+O, Enter, schließen mit Strg+X. Die Zeile PAPERLESS_OCR_LANGUAGES (mit S am Ende) lässt du in Ruhe. Sie installiert zusätzliche Sprachpakete, und Deutsch ist schon drin. Wer dort „deu eng“ einträgt, schickt den Container in eine Endlosschleife beim Start; in den GitHub-Discussions hat ein Maintainer genau das erklärt und geraten, die Zeile ganz wegzulassen. Auch PAPERLESS_URL bleibt auskommentiert, solange du Paperless nur im Heimnetz über die IP aufrufst.
Ist Port 8000 bei dir belegt, öffnest du zusätzlich nano docker-compose.yml und änderst unter ports: die Zeile "8000:8000" in "8010:8000". Nur die erste Zahl ist deine, die zweite ist die im Container und bleibt. Kontrolle, dass in der env-Datei nichts Falsches aktiv ist:
grep -v '^#' docker-compose.env | grep .Der Befehl zeigt nur die Zeilen ohne Raute. Es müssen genau deine fünf sein, und change-me darf nirgends mehr auftauchen.
Schritt 3: Starten und Admin-Konto anlegen
docker compose pull
docker compose up -d
docker compose psDer erste Befehl lädt die beiden Images, der zweite startet die Container im Hintergrund, und dank restart: unless-stopped in der Compose-Datei kommen sie nach einem Neustart des Servers von selbst wieder. Die Ausgabe von docker compose ps listet broker und webserver mit dem Status „Up“ oder „running“. Der erste Start dauert eine Weile, weil Paperless seine Datenbank anlegt; steht der Webserver nach zwei Minuten auf „Restarting“, liest du mit docker compose logs webserver nach, was ihm fehlt. Meist ist es ein Tippfehler in der env-Datei.
Dann in den Browser: http://192.168.178.20:8000, mit deiner IP und deinem Port. Statt einer Anmeldemaske begrüßt dich beim ersten Aufruf eine Seite, die ein Superuser-Konto anlegen will (Stand Paperless-ngx 3.1). Früher brauchte man dafür einen extra Befehl im Terminal; dass das jetzt im Browser geht, spart genau den Schritt, an dem Anfänger hängen blieben.
Benutzername, E-Mail und ein Passwort eintragen, bestätigen. Danach mit genau diesen Daten anmelden. Du landest auf dem Dashboard, noch ohne Dokumente, links die Leiste mit Dokumente, Tags, Korrespondenten und Dokumenttypen.
Wenn das Dashboard steht, ist die Installation durch. Der Rest ist Einrichten, und der ist der interessantere Teil.
Schritt 4: Das erste Dokument
Nimm eine beliebige PDF, gern eine alte Rechnung. Zwei Wege führen sie hinein: Du ziehst sie im Browser aufs Dashboard, oder du kopierst sie in den Eingangsordner. Für den ersten Test würde ich kopieren statt verschieben, dann liegt das Original noch bei dir, falls etwas schiefgeht.
Auf dem Dashboard ist ein Feld zum Hochladen. PDF hineinziehen. Ein Fortschrittsbalken läuft durch, kurz darauf steht das Dokument unter Dokumente in der Liste.
cp ~/rechnung.pdf /opt/paperless/consume/
ls /opt/paperless/consume/Der zweite Befehl zeigt die Datei anfangs noch an. Nach einigen Sekunden bis einer Minute ist der Ordner leer: Paperless hat sie abgeholt. Im Browser öffnest du das Dokument und klickst auf den Reiter Inhalt. Dort steht der erkannte Text. Lies ein paar Zeilen mit: Sind die Umlaute richtig und die Beträge lesbar, war die OCR-Sprache korrekt gesetzt. Steht dort englisch verstümmelter Text, greift noch eng, und du prüfst Schritt 2.
Falls die Datei im Ordner liegen bleibt, gibt docker compose logs --tail 50 webserver die Antwort. Die zwei häufigsten stehen unten bei den Stolpersteinen.
Die ersten Einstellungen
Installieren steht in jeder Doku, Einrichten kaum irgendwo. Vier Dinge würde ich am ersten Abend erledigen, weil sie später nur mit Aufwand nachzuholen sind.
Ein Posteingangs-Tag
Ein Tag ist ein Schlagwort, das du an ein Dokument hängst; anders als ein Ordner darf ein Dokument mehrere davon tragen. Ein Tag ist besonders: der Posteingang. Er markiert alles, was du noch nicht durchgesehen hast. Ohne ihn verschwinden neue Dokumente zwischen den alten, und du weißt in drei Wochen nicht mehr, welche zwanzig noch unsortiert sind. Ein zweiter Grund steckt in der Doku zur automatischen Zuordnung: Sie lernt nur aus Dokumenten, die nicht mehr im Posteingang liegen. Der Tag ist also auch das Signal „an dem hier darfst du dich orientieren“.
Links auf Tags, dann Erstellen. Name Posteingang, eine Farbe, und den Haken bei Posteingangs-Tag (englisch „Inbox tag“) setzen. Speichern. Ab jetzt bekommt jedes neue Dokument diesen Tag automatisch (Stand Paperless-ngx 3.1).
Korrespondenten und Dokumenttypen mit Zuordnung „Auto“
Ein Korrespondent ist der Absender: die Bank, der Vermieter, die Krankenkasse. Der Dokumenttyp sagt, was es ist: Rechnung, Vertrag, Bescheid. Beide legst du selbst an, und bei beiden kannst du einen Zuordnungs-Algorithmus wählen. Der Standardweg über feste Suchwörter funktioniert, aber die Wahl „Auto“ ist der Grund, warum Paperless auf Dauer Arbeit spart: Laut Doku lernt ein kleines neuronales Netz an den Dokumenten, die du schon einsortiert hast, und ordnet neue dann selbst zu. Es braucht dafür „eine vernünftige Anzahl“ an Beispielen und trainiert standardmäßig einmal pro Stunde nach. Die ersten Wochen sortierst du also von Hand, dann übernimmt es zunehmend.
Korrespondenten, dann Erstellen. Name eintragen, beim Zuordnungsalgorithmus „Auto“ wählen, speichern. Dasselbe unter Dokumenttypen für Rechnung, Vertrag und was du sonst regelmäßig bekommst.
Lesbare Dateinamen im Archiv
Standardmäßig heißt deine Rechnung im Speicher 0000001.pdf. Paperless weiß, was das ist, du nicht. Fällt die Datenbank irgendwann aus oder du willst das Programm loswerden, sitzt du vor tausend nummerierten Dateien. Mit einem Dateinamen-Format legt Paperless die Dokumente stattdessen in Ordner nach Jahr und Absender, mit dem Titel als Dateinamen. Das ist die Einstellung, die ich am Anfang setzen würde und nicht später: Änderst du sie nachträglich, musst du laut Doku den Umbenenner von Hand über alle bestehenden Dokumente laufen lassen.
cd /opt/paperless
echo 'PAPERLESS_FILENAME_FORMAT={{ created_year }}/{{ correspondent }}/{{ title }}' >> docker-compose.env
docker compose down
docker compose up -dAus der Rechnung von oben wird damit etwa 2026/Stadtwerke/Jahresabrechnung Strom.pdf. Die Doku warnt an derselben Stelle davor, Dateien im Medienordner je von Hand zu verschieben: Paperless merkt sich den letzten Dateinamen und findet ein verschobenes Dokument nicht wieder.
Ein Backup, das man ohne Paperless lesen kann
Die Dokumente liegen in einem Docker-Volume, die Datenbank in einem zweiten. Beides einzeln zu sichern geht, ist aber unhandlich, und die Datenbank allein sagt dir ohne die passende Paperless-Version wenig. Das Projekt bringt dafür einen Exporter mit, der Dokumente, Vorschaubilder und alle Metadaten samt Einstellungen in einen Ordner schreibt. Den Ordner hast du in Schritt 1 schon angelegt.
docker compose exec webserver document_exporter ../export
ls /opt/paperless/export/Im Export-Ordner liegen danach die PDFs und eine manifest.json mit allem, was Paperless über sie weiß. Diesen Ordner sicherst du wie jeden anderen, auf die NAS, die externe Platte, wohin auch immer. Einschränkung aus der Doku: Ein Export lässt sich nur in dieselbe Paperless-Version zurückspielen, weil er die Datenbank exakt abbildet. Vor jedem Update also einmal exportieren, dann kannst du zurück, wenn das Update klemmt. Ja, das ist wieder der Backup-Satz. Er steht hier, weil der Exporter der einfachste Backup-Weg ist, den ich in einem Selbsthosting-Projekt kenne, und weil ihn trotzdem kaum jemand einrichtet.
Updates selbst sind drei Befehle: docker compose down, docker compose pull, docker compose up -d, in dieser Reihenfolge aus der Doku. Weil in der Compose-Datei latest steht, bringt jeder Pull auch große Versionssprünge mit. Vorher die Release Notes lesen kostet zwei Minuten.
Was hier absichtlich fehlt: der Zugriff von außen. Dafür gehört ein Reverse Proxy davor und PAPERLESS_URL gesetzt; was so ein Proxy ist und warum du ihn willst, steht in Was ist ein Reverse Proxy?. Das ist ein eigener Beitrag, nicht ein Absatz hier.
Wo es erfahrungsgemäß klemmt
„[Errno 13] Permission denied“ im Log, die Datei bleibt im Eingangsordner. Der Klassiker, mit eigenem Abschnitt in der Troubleshooting-Doku: Paperless darf die Datei nach dem Einlesen nicht löschen, weil der Ordner jemand anderem gehört. Prüf mit ls -ln /opt/paperless/consume, welche Nummer als Besitzer steht, und vergleich sie mit USERMAP_UID in der env-Datei. Kommt die Datei per Netzwerkfreigabe von einem NAS, gehört sie oft einem ganz anderen Benutzer; in den Discussions taucht das mit NFS und SMB regelmäßig auf (etwa #3153), und die Antwort des Maintainers dort ist knapp: Container als diesen Benutzer laufen lassen oder dem Ordner die Rechte geben.
Der Eingangsordner liegt auf dem NAS, neue Dateien werden nicht bemerkt. Paperless lauscht normalerweise auf Änderungsmeldungen des Dateisystems, und Netzwerkfreigaben schicken die nicht zuverlässig. In Discussion #5693 erklärt Maintainer stumpylog genau das und verweist auf das regelmäßige Nachsehen statt Lauschen. Die Einstellung heißt in der aktuellen Doku PAPERLESS_CONSUMER_POLLING_INTERVAL, in Sekunden, etwa 30; ältere Forenbeiträge nennen sie noch PAPERLESS_CONSUMER_POLLING. Nach der Änderung Container mit docker compose down und up -d neu starten.
Der Container startet in einer Schleife und lädt ständig Debian-Pakete. Fast immer steht PAPERLESS_OCR_LANGUAGES (mit S) auf einer Sprache, die schon vorinstalliert ist. Zeile entfernen, nur PAPERLESS_OCR_LANGUAGE=deu behalten. Quelle ist Discussion #6731, in der der Melder das Problem nach dem Hinweis sofort los war.
„Forbidden (403) CSRF verification failed“, meist nach dem Umzug hinter einen Reverse Proxy. Dann fehlt PAPERLESS_URL oder steht ohne https:// davor. In den Discussions ist das ein Dauerthema (#5209, #9528); ein Melder hatte die Variablen in Portainer eingetragen, wo sie nie im Container ankamen, und erst der Eintrag in der Compose-Datei half. Solange du Paperless nur über die IP im Heimnetz aufrufst, siehst du diesen Fehler nicht.
Für wen sich Paperless-ngx lohnt
Die Installation ist der leichte Teil, das zeigt schon der Umfang oben: drei Dateien, fünf Zeilen, ein Befehl. Der schwere Teil sind die ersten fünfzig Dokumente, die du von Hand mit Absender und Typ versiehst, damit die Zuordnung etwas zum Lernen hat. Wer dafür ein paar Abende Geduld hat, bekommt ein Archiv, das jede Rechnung in zwei Sekunden findet und das ohne Abo, ohne Cloud und ohne fremde Augen auskommt.
Ich würde es jedem empfehlen, der mehr als einen Ordner Papier im Jahr bekommt und einen Rechner hat, der sowieso läuft. Ich würde davon abraten, wenn du erwartest, dass ein Handyfoto im Halbdunkel zu sauberem Text wird; da ist Tesseract ehrlich überfordert, und das ist kein Fehler von Paperless. Und ich würde die Dateinamen-Einstellung aus dem Einrichtungsteil als Bedingung setzen: Ein Archiv, das ohne sein Programm nicht lesbar ist, ist wieder eine Blackbox, nur diesmal im eigenen Keller.
Womit fütterst du Paperless zuerst: dem Steuerordner, der Post der letzten zwölf Monate oder erst mal nur den neuen Briefen ab heute? Und scannst du mit einem Gerät oder mit dem Handy? Schreib es in die Kommentare, gerade die Handy-Erfahrungen interessieren mich.
Wenn du gerade dabei bist, Dienste ins eigene Netz zu holen: Der Passwort-Manager Vaultwarden ist nach Paperless der zweite Kandidat, der sich an einem Abend erledigen lässt.
Quellen
- Paperless-ngx Doku: Setup (Docker Compose, schwache Geräte)
- Paperless-ngx Doku: Configuration (USERMAP, SECRET_KEY, OCR_LANGUAGE, POLLING_INTERVAL)
- Paperless-ngx Doku: Advanced Usage (Matching, File name handling)
- Paperless-ngx Doku: Administration (Backups, Document exporter, Updating)
- Paperless-ngx Doku: Troubleshooting
- Compose-Dateien im Paperless-ngx-Repository
- Release v3.1.3 (04-09-2026)
- Discussion #5693: Polling statt inotify bei Netzwerkfreigaben
- Discussion #6731: Startschleife durch PAPERLESS_OCR_LANGUAGES
- Discussion #3153: Permission denied im consume-Ordner
- Discussion #5209: CSRF verification failed hinter Reverse Proxy