Papra mit Docker Compose: Dokumentenarchiv selbst hosten, sichern und betreiben
Papra ist ein quelloffenes Dokumentenarchiv mit SQLite, Volltextsuche und OCR in einem einzigen Container. Die Anleitung zeigt Installation per Docker Compose, Absicherung der Registrierung, Ingestion-Ordner, Backup samt Restore sowie Update und Rollback.
Geprüft am 02.10.2026 · für Papra 26.7.0
Mit KI erstellt – redaktionelle Prüfung ausstehend
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Papra ist ein schlankes, quelloffenes Dokumentenarchiv für Rechnungen, Verträge, Lieferscheine und Scans. Papra kommt mit einem einzigen Container aus: SQLite als Datenbank, Dateien im lokalen Dateisystem, Volltextsuche und OCR sind eingebaut. Das Projekt papra-hq/papra steht unter AGPL-3.0, hatte beim Abruf am 2. Oktober 2026 rund 5.524 Sterne auf GitHub, der letzte Push stammt vom 1. Oktober 2026. Am selben Tag erschien die App-Version 26.7.0, die dieser Anleitung zugrunde liegt. Die Anleitung führt von der Installation bis zu Backup, Restore und Update.
Voraussetzungen
Im Test belegte der Container rund 250 MiB RAM; das Image ist 1,34 GB groß, weil Tesseract enthalten ist.
- Linux-Server, VM, Mini-PC oder NAS mit Docker Engine und Docker Compose v2
- CPU: 1 bis 2 Kerne, x86_64 oder ARM; laut Doku gibt es Images für amd64, arm64 und arm/v7
- RAM: 1 GB frei, bei großen OCR-Stapeln eher 2 GB
- Speicher: etwa 2 GB für das Image plus Platz für Ihre Dokumente; die Originale liegen unverändert auf der Platte
- Für Zugriff von außen eine Domain und ein Reverse Proxy mit TLS
Papra kennt Organisationen mit Mitgliedern, API-Schlüssel, Tags, Tagging-Regeln und Freigabelinks. Für ein kleines Büro, das Belege durchsuchbar ablegen will, reicht das; Korrespondenten, Dokumenttypen und Aufbewahrungsfristen bildet Paperless-ngx feiner ab.
Schritt 1: Projektordner, Image-Tag und Geheimnis festlegen
Das offizielle Image läuft rootless. Die Datenordner müssen deshalb dem Benutzer gehören, den Sie in der Compose-Datei angeben.
sudo mkdir -p /opt/papra/app-data/db /opt/papra/app-data/documents /opt/papra/ingestion
sudo chown -R 1000:1000 /opt/papra/app-data /opt/papra/ingestion
cd /opt/papra
openssl rand -hex 48
Vorsicht beim Tag: Die Doku nennt Beispiele wie papra:26.6.2, in der Registry existieren aber nur Varianten mit Suffix. Am 2. Oktober 2026 lieferte GHCR für 26.7.0 ohne Suffix den Status 404, für 26.7.0-rootless dagegen 200; dessen Digest war identisch mit latest. Schreiben Sie daher 26.7.0-rootless fest statt latest.
| Eckdatum | Wert |
|---|---|
| Image | ghcr.io/papra-hq/papra:26.7.0-rootless (Spiegel: corentinth/papra) |
| Port | 1221 (HTTP) |
| Volumes | ./app-data:/app/app-data (SQLite und Originale), ./ingestion:/app/ingestion |
| Pflicht-Env | AUTH_SECRET (mindestens 32 Zeichen), APP_BASE_URL |
| Wichtige Env | AUTH_IS_REGISTRATION_ENABLED, DOCUMENTS_OCR_LANGUAGES, INGESTION_FOLDER_IS_ENABLED |
Verifizieren: ls -ln /opt/papra/app-data zeigt die Ordner db und documents mit Eigentümer 1000:1000.
Schritt 2: compose.yaml und .env anlegen
Die folgende Datei baut auf dem offiziellen Compose-Beispiel auf. Ergänzt sind Ingestion-Ordner, OCR-Sprachen, Healthcheck und eine .env. Das Image hat weder Healthcheck noch curl, daher prüft Node den Endpunkt /api/health.
services:
papra:
image: ghcr.io/papra-hq/papra:${PAPRA_VERSION}
container_name: papra
restart: unless-stopped
user: "${PAPRA_UID}:${PAPRA_GID}"
ports:
- "127.0.0.1:1221:1221"
environment:
- AUTH_SECRET=${AUTH_SECRET}
- APP_BASE_URL=${APP_BASE_URL}
- AUTH_IS_REGISTRATION_ENABLED=${AUTH_IS_REGISTRATION_ENABLED}
- DOCUMENTS_OCR_LANGUAGES=${DOCUMENTS_OCR_LANGUAGES}
- INGESTION_FOLDER_IS_ENABLED=true
- INGESTION_FOLDER_POST_PROCESSING_STRATEGY=move
volumes:
- ./app-data:/app/app-data
- ./ingestion:/app/ingestion
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://localhost:1221/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 30s
timeout: 5s
start_period: 30s
retries: 3
PAPRA_VERSION=26.7.0-rootless
PAPRA_UID=1000
PAPRA_GID=1000
# Ausgabe von: openssl rand -hex 48
AUTH_SECRET=HIER_IHR_GEHEIMNIS_EINTRAGEN
APP_BASE_URL=https://papra.example.de
# Erst true für das Admin-Konto, danach false
AUTH_IS_REGISTRATION_ENABLED=true
DOCUMENTS_OCR_LANGUAGES=deu,eng
Der Port ist an 127.0.0.1 gebunden, weil der Reverse Proxy auf demselben Host läuft. Ohne DOCUMENTS_OCR_LANGUAGES erkennt Papra nur Englisch. Setzen Sie die Rechte der .env mit chmod 600 .env.
Verifizieren: docker compose config gibt die Konfiguration ohne Fehlermeldung aus und zeigt die eingesetzten Werte.
Schritt 3: Starten und Funktion prüfen
docker compose up -d
docker compose ps
docker compose logs --tail 30
curl -s http://127.0.0.1:1221/api/health
Beim ersten Start spielt Papra die Datenbankmigrationen ein, im Log stehen All migrations run successfully und Server started mit Port 1221. Bei aktivem Ingestion-Ordner meldet das Log zusätzlich Ingestion folder watcher started.
Verifizieren: docker compose ps zeigt nach etwa 30 Sekunden Up (healthy), und der Health-Endpunkt antwortet mit {"isDatabaseHealthy":true,"isEverythingOk":true,"status":"ok"}.
Schritt 4: Reverse Proxy mit TLS
Papra spricht nur HTTP, die Session-Cookies tragen aber das Präfix __Secure-. Mit Caddy genügt:
papra.example.de {
reverse_proxy 127.0.0.1:1221
}
APP_BASE_URL muss exakt der Adresse entsprechen, die im Browser steht, inklusive https://. Weitere Adressen tragen Sie kommagetrennt in TRUSTED_ORIGINS ein. Stimmt die Adresse nicht, meldet Papra laut Doku beim Login einen ungültigen Application Origin. Die Client-IP für das Rate-Limiting liest Papra standardmäßig aus X-Forwarded-For.
Verifizieren: curl -I https://papra.example.de liefert HTTP/2 200, die Anmeldeseite lädt im Browser ohne Zertifikatswarnung.
Schritt 5: Admin-Konto anlegen und Registrierung schließen
Öffnen Sie die Adresse und registrieren Sie das erste Konto. Es erhält automatisch Admin-Rechte (AUTH_FIRST_USER_AS_ADMIN ist standardmäßig true) und sieht oben den Punkt „Verwaltung“ mit Statistiken, Benutzern und Organisationen. Danach legen Sie eine Organisation an, etwa mit dem Firmennamen. Wichtig: Die Registrierung bleibt nach dem ersten Konto offen. Im Test ließ sich direkt danach ein zweites Konto ohne Anmeldung über die API anlegen, Antwort HTTP 200. Schalten Sie das ab:
sed -i 's/^AUTH_IS_REGISTRATION_ENABLED=.*/AUTH_IS_REGISTRATION_ENABLED=false/' .env
docker compose up -d
Weitere Personen laden Sie anschließend über „Mitglieder“ in die Organisation ein. Vor 26.6.2 blendete die Option laut Release Notes nur das Formular aus, die Auth-API nahm weiter Registrierungen an.

Verifizieren: Ein Registrierungsversuch über /api/auth/sign-up/email endet mit HTTP 400 und "code":"EMAIL_PASSWORD_SIGN_UP_DISABLED", die Seite /register zeigt „Registrierung ist deaktiviert“, die Anmeldung bestehender Konten funktioniert weiter.
Schritt 6: Dokumente hochladen, OCR und Suche nutzen
Laden Sie über „Dokument importieren“ oder per Drag-and-drop Dateien hoch. Papra extrahiert den Text sofort mit der eingebauten Bibliothek lecture und greift bei Bildern und gescannten PDFs auf Tesseract zurück. Im Test fand die Suche nach einem Wort, das nur im Bild stand, sowohl den PNG-Scan als auch das PDF ohne Textebene.


Den erkannten Text sehen Sie in der Dokumentansicht im Reiter „Inhalt“. Seit 26.7.0 gibt es dort die Schaltfläche „Reprocess document“, die Extraktion, Auto-Tagging und Tagging-Regeln erneut ausführt, etwa nachdem Sie die OCR-Sprachen geändert haben. Alles im Dokument landet durchsuchbar im Index, auch Kontodaten.

Die Standardgrenze für Uploads liegt bei 26.214.400 Byte, also 25 MiB (DOCUMENT_STORAGE_MAX_UPLOAD_SIZE). Größere Scans erfordern einen höheren Wert, auch im Reverse Proxy.
Verifizieren: Laden Sie einen Scan mit einem eindeutigen Wort hoch und suchen Sie danach; das Dokument erscheint in der Trefferliste, der Reiter „Inhalt“ zeigt den erkannten Text.
Schritt 7: Ingestion-Ordner für Scanner und Netzlaufwerke
Der Ingestion-Ordner nimmt Dateien automatisch auf, etwa von einem Netzwerkscanner. Entscheidend ist die Struktur: Jede Organisation braucht einen Unterordner mit ihrer ID, die Sie in der Adresszeile finden (/organizations/org_...).
mkdir -p /opt/papra/ingestion/org_IHRE_ID
cp rechnung.pdf /opt/papra/ingestion/org_IHRE_ID/
sudo chown -R 1000:1000 /opt/papra/ingestion
Dateien direkt im Wurzelordner ignoriert Papra. Im Log steht dann A file in the ingestion folder is not located in an organization ingestion folder, skipping. Mit INGESTION_FOLDER_POST_PROCESSING_STRATEGY=move verschiebt Papra verarbeitete Dateien nach ingestion-done, fehlerhafte landen in ingestion-error. Die Voreinstellung delete löscht die Quelldatei nach dem Import. Auf SMB- oder NFS-Freigaben hilft INGESTION_FOLDER_WATCHER_USE_POLLING=true.
Verifizieren: Nach wenigen Sekunden liegt die Datei unter org_IHRE_ID/ingestion-done/ und ist in Papra über die Suche auffindbar.
Schritt 8: Backup und Restore
Alle Daten liegen in app-data: die SQLite-Datenbank unter db/db.sqlite und die Originale unter documents/org_.../originals/. Datenbank und Dateien gehören zusammen, sichern Sie beide im selben Zustand. Am einfachsten stoppen Sie den Container kurz, damit SQLite konsistent ist:
cd /opt/papra
docker compose stop
tar czf /backup/papra-$(date +%F).tgz app-data .env compose.yaml
docker compose start
Die .env gehört ins Backup, aber verschlüsselt oder an einem geschützten Ort, denn sie enthält das AUTH_SECRET. Ohne Dokumentverschlüsselung hängt an diesem Wert nur die Gültigkeit der Sitzungen: Im Test führte ein neues Geheimnis bei alten Sitzungen zu HTTP 401, nach erneuter Anmeldung waren alle Dokumente da. Aktivieren Sie die Verschlüsselung mit DOCUMENT_STORAGE_ENCRYPTION_IS_ENABLED=true, ist der Schlüssel in DOCUMENT_STORAGE_DOCUMENT_KEY_ENCRYPTION_KEYS laut Doku zum Entschlüsseln zwingend nötig. Geht er verloren, sind die Dateien unlesbar.
So stellen Sie wieder her:
cd /opt/papra
docker compose down
mv app-data app-data.defekt
tar xzpf /backup/papra-2026-10-02.tgz app-data
docker compose up -d
Im Test war ein gesichertes, danach gelöschtes Marker-Dokument nach dem Entpacken wieder auffindbar und herunterladbar; ein nach dem Backup hochgeladenes Dokument fehlte erwartungsgemäß.
Verifizieren: Nach dem Restore ist docker compose ps wieder healthy, die Suche nach einem Dokument aus der Zeit vor dem Backup liefert einen Treffer und der Download zeigt den Originalinhalt.
Schritt 9: Update und Rollback
Papra nutzt Versionen im Schema YY.MINOR.PATCH und veröffentlicht laut Doku keine Sicherheitskorrekturen für ältere Versionen. Updates sind also Pflicht. Version 26.7.0 bringt unter anderem einen API-Endpunkt und eine Schaltfläche zum erneuten Verarbeiten von Dokumenten, gebündelte Schriften für den Offline-Betrieb, neue Platzhalter wie {{organization.name}} und document.date für Speicherpfade, die optionale Synchronisation mit DOCUMENT_STORAGE_KEY_SYNC_ENABLED=true, DOCLING_OPTIONS für Docling-OCR und eine Sicherheitskorrektur: Eigene OAuth-Anbieter konnten bei abgeschalteter Registrierung neue Konten anlegen. Achtung für Skripte: DELETE auf ein Dokument liefert jetzt 204 ohne JSON-Antwort.
cd /opt/papra
docker compose stop
tar czf /backup/papra-vor-update.tgz app-data .env compose.yaml
sed -i 's/^PAPRA_VERSION=.*/PAPRA_VERSION=26.7.1-rootless/' .env
docker compose pull
docker compose up -d
Die Versionsnummer im Beispiel ist ein Platzhalter für das nächste Release. Ein Rollback auf das alte Image ist nicht garantiert. Im Test startete 26.6.2 zwar auf den Daten von 26.7.0 und meldete All migrations already applied, ältere Versionen kennen neue Tabellen und Spalten aber nicht. Der verlässliche Weg zurück ist das alte Tag zusammen mit dem Backup von vor dem Update.
Verifizieren: docker compose ps zeigt das neue Tag und healthy, das Log enthält die Migrationen und Server started.
Typische Fehler
- Container startet ständig neu:
Invalid configuration: auth.secret (AUTH_SECRET): Invalid length: Expected >=32 but received 0. Das Geheimnis fehlt oder ist zu kurz; bei einem zu kurzen Wert steht dort die tatsächliche Länge. - Container beendet sich mit Exit 1 ohne gesetztes
AUTH_SECRET:In production, the auth secret must not be the default one.Papra verweigert den eingebauten Standardwert. - Start scheitert mit EACCES:
Failed to ensure that the database directory exists, error while creating the directoryundUnable to open connection to local database ./app-data/db/db.sqlite: 14. Der Ordner gehört root; Abhilfe istchown -R 1000:1000 app-data. - Anmeldung liefert HTTP 500, das Log zeigt
DrizzleQueryError: Failed query: insert into "auth_sessions": Die Datenbank ist vorhanden, aber nicht beschreibbar, typischerweise nach einem Restore als root. Der Container bleibt dabeiUp. - Registrierung schlägt mit
attempt to write a readonly databasefehl: Die Datenbankdatei wurde bei laufendem Container ersetzt oder gelöscht. Container stoppen, Dateien zurückspielen, neu starten. manifest unknownbeim Pull: Das Tag ohne-rootlessoder-rootexistiert nicht.
Häufige Fragen
Rootless oder root, welche Variante sollte ich nehmen?
Die Doku empfiehlt rootless. Root brauchen Sie nur, wenn Sie Besitzrechte auf dem Host nicht anpassen können, etwa auf manchen NAS-Systemen.
Ersetzt Papra Paperless-ngx?
Für einfache Ablage mit Suche ja, bei deutlich weniger Betriebsaufwand. Für komplexe Workflows bleibt Paperless-ngx stärker.
Kann ich PostgreSQL oder S3 nutzen?
Die Datenbank ist SQLite beziehungsweise libSQL über DATABASE_URL. Für Dokumente kennt DOCUMENT_STORAGE_DRIVER neben filesystem auch s3 und azure-blob.
Wird die KI für das Auto-Tagging zwingend benötigt?
Nein. Ohne LLM-Anbieter arbeitet Papra lokal, Tagging-Regeln funktionieren trotzdem.
Testumfang
Praktisch getestet wurden mit Papra 26.7.0-rootless auf Debian 12 der Start per Compose samt Healthcheck, der Zugriff über einen vorgeschalteten Reverse Proxy mit TLS, Admin-Konto, offene und geschlossene Registrierung, Organisation, Upload mit OCR und Suche, Ingestion-Ordner, Backup und Restore mit Marker, ein Wechsel des AUTH_SECRET, die zitierten Fehlerbilder sowie ein Start von 26.6.2 auf Daten von 26.7.0. Nur aus der Dokumentation stammen die Caddy-Konfiguration, Dokumentverschlüsselung, Polling auf Netzlaufwerken, externe OCR-Anbieter, Speicher-Backends und LLM-Funktionen.
Fazit
Papra ist eine der schlankesten Möglichkeiten, Dokumente selbst gehostet durchsuchbar abzulegen: ein Container, ein Datenordner, OCR ohne Zusatzdienste. Die wichtigsten Handgriffe nach der Installation sind ein ausreichend langes AUTH_SECRET, das Schließen der Registrierung, deutsche OCR-Sprachen und ein regelmäßiges Backup von app-data. Da ältere Versionen keine Korrekturen erhalten, gehören regelmäßige Updates mit vorherigem Backup dazu.
Weiterführende Anleitungen und Quellen
- Paperless-ngx mit Docker: Dokumentenverwaltung mit OCR
- Rechnungen lokal mit Vision-LLM und OCR auslesen
- Pangolin mit Docker als Tunnel und Reverse Proxy
- GitHub: papra-hq/papra
- Release Notes @papra/app@26.7.0 vom 1. Oktober 2026
- Papra-Doku: Using Docker Compose
- Papra-Doku: Konfigurationsvariablen
- Papra-Doku: Ingestion-Ordner
- Papra-Doku: Troubleshooting


