Papra
Paper arrives, gets photographed or scanned, and lands in a folder called Scans where it is never found again. Papra is a document archive: throw files at it, and it makes them searchable, tagged and organised.
It positions itself as the minimalistic option in a category that also contains Paperless-ngx — a deliberate trade of features for a much simpler deployment.
Papra is a minimalistic document archiving platform.
- Full-text search across your documents
- Tags and organisations for structure, with multi-tenant separation
- Tagging rules that apply tags automatically based on content
- Drag-and-drop ingestion, plus an intake folder for automated imports
- Single container — SQLite and local file storage, no separate database or search engine
- Rootless image, which is a nicer default than most
Papra or Paperless-ngx?
Both archive documents. The difference is scope:
| Papra | Paperless-ngx | |
|---|---|---|
| Containers | 1 | 3–4 (app, database, Redis, sometimes Tika) |
| OCR | Basic | Extensive, with language packs |
| Maturity | Younger | Long-established, very large community |
| Resource use | Light | Noticeably heavier |
| Setup time | Minutes | An afternoon |
If you are digitising decades of paper and need serious OCR, Paperless-ngx is the stronger tool. If you want somewhere sensible to put PDFs that are already text — invoices, statements, warranties, manuals — Papra does that with a fraction of the operational weight.
Prerequisites
- Docker and the Compose plugin — see the Docker guide
opensslfor the auth secret
1. Create the data directories
The rootless image runs as your user, so the directories need to exist with the right ownership first:
2. Generate the auth secret
3. The Compose file
Upstream documents docker run; this is the equivalent Compose:
services:
papra:
image: ghcr.io/papra-hq/papra:latest
container_name: papra
restart: unless-stopped
user: "1000:1000"
ports:
- 1221:1221
volumes:
- ./papra-data:/app/app-data
environment:
APP_BASE_URL: http://192.168.1.20:1221
AUTH_SECRET: ${AUTH_SECRET}
Notes:
user: "1000:1000"should match the owner ofpapra-data. Runid -uandid -gto check. On Unraid use99:100.APP_BASE_URLmust be the address you actually browse to. Wrong value breaks login redirects and share links.- Port 1221 is unusual enough to avoid every collision in this documentation.
- Everything — database and documents — lives under
/app/app-data, so there is exactly one thing to back up.
4. Create your account
Browse to http://<host-ip>:1221 and register. The first account is yours.
Once created, consider disabling registration so the instance is not open — check the configuration docs for the current variable name, as this project is moving quickly.
5. Create an organisation
Papra groups documents into organisations. Even for personal use, create one — it is the container everything else lives in, and documents cannot exist outside it.
For a household, one organisation per person keeps things separate while sharing an instance.
6. Ingest documents
Two routes:
Drag and drop into the web UI. Fine for occasional additions.
The intake folder is the better one for anything regular. Papra watches a directory and imports what appears in it. Mount a share your scanner writes to:
Then point your scanner or phone app at that share, and documents file themselves. Check the current documentation for the intake configuration variable — this is a newer feature and the name has changed.
7. Tagging rules
Settings → Tagging rules applies tags automatically based on document content — a document containing "British Gas" gets the utilities tag, one containing "Invoice" gets invoices.
Set these up before bulk-importing. Rules applied to 400 documents at once is a much better experience than tagging 400 documents by hand, and the temptation is always to import first and organise later.
8. Put it behind Traefik
environment:
APP_BASE_URL: https://docs.example.com
AUTH_SECRET: ${AUTH_SECRET}
networks:
- traefik-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.papra.rule=Host(`docs.example.com`)"
- "traefik.http.routers.papra.entrypoints=https"
- "traefik.http.routers.papra.tls.certresolver=letsencrypt"
- "traefik.http.services.papra.loadbalancer.server.port=1221"
- "traefik.docker.network=traefik-net"
networks:
traefik-net:
external: true
Change APP_BASE_URL at the same time. Papra has its own accounts, so no Tinyauth needed — but it holds your bank statements and contracts, so keep it LAN-only or behind wg-easy.
Updating
Papra is a young project and moves fast. Pin a version tag rather than latest once you have data in it, read the release notes, and back up before upgrading. The convenience of latest is not worth an unattended schema migration on your document archive.
Backup
One directory, which is the nicest thing about this deployment:
cd /opt/docker/papra
docker compose stop
sudo tar czf /mnt/user/backups/papra-$(date +%F).tar.gz papra-data .env
docker compose start
Stop it first — SQLite. The archive contains the database and the documents, so a restore is a single extract.
The documents are stored as regular files under papra-data/documents, which is worth knowing: even if Papra itself became unusable, your files are still there in a normal filesystem. That is a meaningful property for an archive you intend to keep for years, and one reason to prefer it over systems that store blobs in a database.
Troubleshooting
Permission denied on startup. The user: value does not match the owner of papra-data. ls -ln papra-data and compare, then chown -R.
Login loops or redirects to the wrong host. APP_BASE_URL does not match the address in your browser.
"Invalid auth secret" or sessions dropping. AUTH_SECRET changed, or is unset. Changing it logs everyone out.
Uploads fail for large files. Reverse proxy body size limit. Traefik does not limit by default; NPM does — raise client_max_body_size in the host's advanced config.
Search returns nothing for a document you can see. It is a scanned image with no text layer. Papra's OCR is basic; run the PDF through an OCR tool first, or use Paperless-ngx if this is your main use case.
Documents visible but not downloadable. File permissions inside papra-data/documents.
Where this sits in my lab
Papra runs on the Docker VM on my first Proxmox node, LAN-only, with an intake folder on the Unraid box that the scanner writes to.
It handles the documents that are born digital — invoices, statements, warranties, manuals — where there is already a text layer and the only real problem is findability. That is most of what arrives now, and it needs neither the OCR pipeline nor the four containers that a heavier system would bring with it.