pless¶
A searchable archive of your documents, on hardware you own.
Paperless-ngx is a document management system for the paper that accumulates in a life: invoices, receipts, contracts, diplomas, letters from institutions that still send letters. You feed it scans and PDFs; it runs OCR over them so the text inside becomes searchable, then sorts them by correspondent, document type and date — guessing sensibly, and learning from your corrections. What you get back is a filing cabinet you can grep, reachable from a browser or a phone.
It is open source and self-hosted, which is the point: your tax returns and your children's birth certificates stay on hardware you control rather than in someone else's product roadmap.
pless sets that up and operates it — on a Raspberry Pi in your home, a cloud server, or a
local VM — with two properties most self-hosting guides skip:
- Encrypted at rest. Your documents live on a LUKS2-encrypted volume. The key is never stored on the machine. Someone who walks off with your Pi gets hardware and an operating system, not your tax returns.
- Invisible on your network. After hardening, nothing listens on your LAN. Paperless is bound to localhost, the database publishes no ports at all, and SSH accepts connections only over your Tailscale network. A compromised device on your Wi-Fi finds nothing to attack.
Alpha — read this before you rely on it
pless is under active development and cannot yet import documents or take backups.
Those are the next two milestones. What works today is documented here; what doesn't is
listed in Status. Don't put your only copy of anything on it yet.
What it looks like¶
$ pless doctor
✓ Python 3.12.3 (requires 3.12+)
✓ ssh found in PATH
✓ pless.toml found
✓ SSH key found (~/.ssh/id_ed25519)
Host
✓ [host] points at archive.local
✓ SSH key found (~/.ssh/id_ed25519)
✓ [storage] data_mode=file (100 GB LUKS file)
All clear. Next: pless bootstrap
$ pless audit
✓ listening sockets: Nothing listens outside loopback — your LAN sees zero ports.
✓ firewall: UFW active, default deny, no LAN-open rules.
✓ docker ports: No container publishes outside loopback.
✓ ssh: Key-based authentication only.
✓ encrypted storage: Data lives on the LUKS device /dev/mapper/paperless-data.
The whole stack¶
Everything below the dashed line runs on your machine. Nothing in it is reachable from your local network once hardening is applied.
flowchart TB
subgraph laptop["Your laptop"]
cli["<b>pless</b><br/><small>Python 3.12 · Typer · Rich<br/>Pydantic · httpx</small>"]
docs["Your documents<br/><small>PDF · images · Office</small>"]
end
subgraph tailnet["Tailscale tailnet — WireGuard"]
ts["MagicDNS + ACLs"]
end
subgraph host["Target: Raspberry Pi · Hetzner · local VM"]
subgraph os["Debian 13 or Ubuntu 24.04 · arm64 or x86_64"]
sec["<b>Hardening</b><br/><small>UFW · fail2ban · OpenSSH keys only<br/>unattended-upgrades</small>"]
luks["<b>LUKS2 volume</b><br/><small>AES-XTS or Adiantum · ext4<br/>key never stored on device</small>"]
subgraph compose["Docker Compose — bound to 127.0.0.1"]
web["paperless-ngx<br/><small>Django · OCRmyPDF · Tesseract</small>"]
pg[("PostgreSQL 16")]
redis[("Redis 7")]
tika["Apache Tika"]
got["Gotenberg"]
end
end
end
subgraph offsite["Off-site — planned"]
restic["restic<br/><small>encrypted · incremental</small>"]
b2[("Backblaze B2")]
end
cli -- "SSH · provisioning and day-2 ops" --> host
docs -. "REST API upload (planned)" .-> web
cli --- ts
ts --- host
web --- pg
web --- redis
web --- tika
web --- got
compose --- luks
luks -. "nightly snapshot (planned)" .-> restic
restic --> b2
classDef planned stroke-dasharray: 5 5
class offsite,restic,b2 planned
Why not just run Docker Compose yourself?¶
You can, and many people do. pless exists because the interesting parts are not the
docker compose up — they are everything around it: making the disk encrypted without
storing the key on the box, making sure a reboot doesn't silently start the stack against
an unlocked volume, keeping the firewall honest when Docker writes its own iptables rules
behind your back, and having a single command that tells you whether any of that has
quietly stopped being true.
Where to go next¶
-
What you need, what it costs, and a ten-minute overview of the whole flow.
-
Step by step, from an unboxed Raspberry Pi to a running archive.
-
Day-to-day operation: updates, health checks, logs, disk usage.
-
Recipes for the situations you will actually run into.
Status¶
pless is verified end to end on Debian 13 and Ubuntu 24.04. Here is the honest state of
each piece:
| Capability | Status |
|---|---|
| Provisioning (Docker, firewall, fail2ban, auto-updates) | Working |
| Encrypted storage with LUKS2 | Working |
| Deploying the Paperless-ngx stack | Working |
| Tailscale access and LAN hardening | Working |
| Exposure auditing | Working |
Readiness gate (pless preflight) |
Working |
| Raspberry Pi as a target | Documented, awaiting hardware validation |
| Hetzner Cloud as a target | Code and unit tests, not validated live |
| Importing your documents | Not built |
| Backup, restore, and restore drills | Not built |
| Alternative unlock methods | Planned |
| Password-manager integration | Planned |
| Web setup wizard | Planned |
What comes next¶
Import is the immediate priority — without it there is no archive. Backup follows,
and restore drills ship with it rather than after it, because a backup that has never been
restored is a belief rather than a backup. Until both exist, pless preflight deliberately
refuses to give a green light.
Backup will target either a local directory or off-site object storage, and the machine will hold a key that cannot delete what it has already written — otherwise whoever takes the machine takes the rescue with it.
What is being worked on, and the reasoning behind each piece, lives in the issue tracker. This page says what works today; the tracker says what is being done about the rest.