259 lines
12 KiB
Markdown
259 lines
12 KiB
Markdown
# Paperless-ngx
|
||
|
||
Document archive with OCR. Runs on the OptiPlex homeserver (`192.168.30.20`)
|
||
in `~/paperless`, reachable only as `https://paperless.home.staffenberger.at`,
|
||
at home or through the WireGuard VPN.
|
||
|
||
## How the pieces fit
|
||
|
||
```
|
||
Brother scanner (IoT) ──SMB──▶ NAS share `scans` ──SMB mount──▶ Paperless consume
|
||
│
|
||
NAS share `paperless-export` ◀──nightly document_exporter────────────┘
|
||
│
|
||
└──▶ Hyper Backup to the USB drive (manual, every two weeks)
|
||
```
|
||
|
||
- **`scans`** is only an inbox. Paperless polls it every 60 s, imports
|
||
each file, and **deletes it from the share** afterwards. The documents
|
||
themselves live in `./media` on the OptiPlex.
|
||
- **`paperless-export`** gets a full export every night: originals, archived
|
||
PDFs and a manifest with tags, correspondents and so on. `document_importer`
|
||
can rebuild a fresh instance from it, so this export is the backup. No
|
||
extra copy of `./data` or `./media` is needed.
|
||
- The database (SQLite) stays on the local disk. A database on a network
|
||
share is asking for corruption.
|
||
|
||
**Sorting** works through matching rules on tags, correspondents and document
|
||
types. The default mode, *Auto*, learns from the documents you've already
|
||
assigned. So at first you assign by hand; after a few dozen documents it
|
||
starts suggesting assignments. It doesn't sort anything correctly out of the box.
|
||
|
||
## NAS (DSM)
|
||
|
||
1. **Two shared folders:** `scans` and `paperless-export`. Hide them from
|
||
"My Network Places", **no recycle bin on `scans`**. Every imported file
|
||
would end up there, and worse: DSM restricts `#recycle` to admins, so
|
||
Paperless's recursive watcher crashes on it with `Permission denied
|
||
(os error 13)` (hit 2026-10-05). If `#recycle` exists, delete it in
|
||
File Station after turning the recycle bin off.
|
||
2. **Two users**, each with no other permissions and no app access besides SMB:
|
||
- `scanner`: read/write on `scans` only. Its password is stored in the
|
||
Brother scanner.
|
||
- `paperless`: read/write on `scans` and `paperless-export`.
|
||
|
||
Gotchas (set up 2026-10-05):
|
||
- The `users` group allows almost every application by default, so new
|
||
users start with full app access. Set **Deny** on everything except
|
||
**SMB** in each user's Applications tab, and check the Preview column.
|
||
Don't deny SMB too, or the user can't log in at all.
|
||
- In the shared folder's permissions (Local groups view), leave the
|
||
`users` group with **no box ticked**. Its Preview then shows "No Access",
|
||
which only means "grants nothing". Ticking **No access** would be an
|
||
explicit deny, which always wins in DSM, and would lock out `scanner`
|
||
and `paperless`, who are both in `users`.
|
||
3. **Hyper Backup:** add `paperless-export` to the existing task's sources.
|
||
New shared folders are **not** picked up automatically, even though "all
|
||
shared folders" was selected when the task was created. `scans` doesn't
|
||
need backing up.
|
||
|
||
## Scanner (Brother) → NAS
|
||
|
||
The scanner sits on IoT, and IoT Outwards (`IoT → !IoT` deny) blocks it from the
|
||
NAS. It needs one narrow exception **above IoT Outwards** (in place since 2026-10-05 as "Printer to NAS SMB"):
|
||
|
||
| Policy | Protocol | Source | Destination |
|
||
|---|---|---|---|
|
||
| Allow | TCP | IP group: scanner's reserved IP | IP-Port group: `192.168.30.10` port `445` |
|
||
|
||
Groups are under Settings → Profiles → Groups; the rule goes in the Gateway
|
||
ACL, LAN→LAN. (Check the menu paths in Controller 6.3.)
|
||
|
||
Then in the scanner's web interface (Web Based Management): Scan → Scan to
|
||
FTP/SFTP/Network/SharePoint → set a profile to **Network**, host
|
||
`192.168.30.10`, store directory `scans`, user `scanner`, authentication
|
||
NTLMv2, file type PDF, 300 dpi. Exact menu names depend on the model.
|
||
|
||
The scanner is a **Brother MFC-L3750CDW**. **Working since 2026-10-05** over
|
||
SMB with DSM's default minimum of SMB2: Profile 1 = Network,
|
||
`\\192.168.30.10\scans`, NTLMv2, user `scanner`, firmware MAIN
|
||
`ZJ2606120536` (already the latest).
|
||
|
||
- **No SFTP on this model** (the menu is "Scan to FTP/Network/SharePoint").
|
||
If SMB ever breaks, **don't turn SMB1 on in DSM**. Fallback: plain FTP,
|
||
port 21 + passive range in the ACL. The `scanner` password would then
|
||
cross the network in plain text, which is tolerable only because that user
|
||
can do nothing except write to `scans`.
|
||
- **Chrome autofill on the printer's web interface:** Chrome filled its saved
|
||
printer login into the profile's username/password fields and then
|
||
reported a "breached password" on Submit. That was most likely the old
|
||
default admin password. Paste the `scanner` login from Enpass into cleared
|
||
fields, and don't let Chrome save the printer's passwords.
|
||
- Time on the scanner (SNTP) has to be right, or authentication can fail.
|
||
- Optional: one profile per person, saving into `scans/David` and
|
||
`scans/<partner>`. Because of `CONSUMER_SUBDIRS_AS_TAGS`, the documents
|
||
then come in tagged with the person's name.
|
||
|
||
## OptiPlex
|
||
|
||
The shares are mounted on the host with **systemd automount**, not at boot:
|
||
after a power cut the OptiPlex comes up long before the NAS, and a normal
|
||
mount would just fail and stay that way. The automount connects on first
|
||
access and tries again later if the NAS isn't up yet.
|
||
|
||
1. `sudo apt install cifs-utils`
|
||
2. Credentials file, readable by root only:
|
||
|
||
```bash
|
||
sudo install -m 600 /dev/null /etc/nas-paperless.cred
|
||
sudoedit /etc/nas-paperless.cred
|
||
```
|
||
|
||
Contents:
|
||
|
||
```
|
||
username=paperless
|
||
password=...
|
||
```
|
||
|
||
3. `sudo mkdir -p /mnt/nas/scans /mnt/nas/paperless-export`, then add to
|
||
`/etc/fstab`:
|
||
|
||
```
|
||
//192.168.30.10/scans /mnt/nas/scans cifs credentials=/etc/nas-paperless.cred,uid=1000,gid=1000,file_mode=0660,dir_mode=0770,vers=3.0,_netdev,nofail,x-systemd.automount,x-systemd.idle-timeout=0 0 0
|
||
//192.168.30.10/paperless-export /mnt/nas/paperless-export cifs credentials=/etc/nas-paperless.cred,uid=1000,gid=1000,file_mode=0660,dir_mode=0770,vers=3.0,_netdev,nofail,x-systemd.automount,x-systemd.idle-timeout=0 0 0
|
||
```
|
||
|
||
`uid`/`gid` 1000 is the container's user (`USERMAP_UID`); check that
|
||
`id -u` on the OptiPlex is 1000 as well.
|
||
4. `sudo systemctl daemon-reload && sudo systemctl restart remote-fs.target`,
|
||
then `ls /mnt/nas/scans` (should be empty, no error).
|
||
|
||
## Setup
|
||
|
||
1. `cp .env.example .env`, fill in the secret key.
|
||
2. `docker compose up -d`. The `proxy` network already exists from the
|
||
Vaultwarden setup.
|
||
3. Check the mount reaches the container:
|
||
`docker compose exec webserver ls -la /usr/src/paperless/consume`
|
||
4. Admin user (the password stays out of the compose file):
|
||
`docker compose exec webserver createsuperuser`. Later, make a normal
|
||
user for each person in the web UI.
|
||
5. **Pi-hole Local DNS record:** `paperless.home.staffenberger.at` →
|
||
`192.168.30.20`.
|
||
6. **NPM proxy host:** `paperless.home.staffenberger.at`, scheme `http`,
|
||
forward to `paperless` port `8000` (container name, as with Vaultwarden),
|
||
**Websockets Support on** (live status of imports), wildcard certificate,
|
||
Force SSL, HTTP/2.
|
||
7. Scan one test page and check it shows up in Paperless within a minute
|
||
and disappears from `scans`.
|
||
|
||
Steps 1–7 done 2026-10-05.
|
||
|
||
## Users and separating documents
|
||
|
||
Each person scans with their own profile on the printer. The file lands in
|
||
their subfolder, and a workflow makes them the owner, so only they see it.
|
||
Scans from the root folder (profile `NAS`) get no owner and are visible to
|
||
both of us.
|
||
|
||
```
|
||
Printer profile Folder on the NAS Owner in Paperless
|
||
NAS scans/ none → shared
|
||
David scans/David/ david
|
||
Denise scans/Denise/ denise
|
||
```
|
||
|
||
**Not a security boundary:** the superuser sees everything, and so does
|
||
anyone with access to the OptiPlex disk or the export on the NAS. It keeps
|
||
daily use tidy and private between the two of us, nothing more.
|
||
|
||
Steps:
|
||
|
||
1. **Group `Household`** (Settings → Users & Groups → Groups). Global
|
||
permissions View/Add/Change/Delete on: Documents, Tags, Correspondents,
|
||
Document types, Storage paths, Saved views, Custom fields, Notes, Share
|
||
links, UI settings. **Not**: Users, Groups, Workflows, Mail accounts/rules,
|
||
App config, Admin.
|
||
2. **Normal users `david` and `denise`**, both in `Household`, neither
|
||
superuser. The admin account is only for administration; otherwise
|
||
Denise's documents show up in my daily view.
|
||
3. **Subfolders** `scans/David` and `scans/Denise` in File Station. The
|
||
printer probably doesn't create folders itself.
|
||
4. **Printer profiles** (web interface, Scan to FTP/Network/SharePoint
|
||
Profile): Profile 2 `David` → `\\192.168.30.10\scans\David`, Profile 3
|
||
`Denise` → `\\192.168.30.10\scans\Denise`, both set to Network, otherwise
|
||
like Profile 1 (paste the `scanner` login into cleared fields, see the
|
||
Chrome autofill note above).
|
||
5. **Workflows** (Settings → Workflows), one per person:
|
||
|
||
| Field | Value |
|
||
|---|---|
|
||
| Trigger | Consumption Started |
|
||
| Sources | Consume Folder |
|
||
| Filter path | `*/David/*` (or `*/Denise/*`) |
|
||
| Action | Assignment → Owner `david` (or `denise`) |
|
||
|
||
6. **Default permissions:** each user, under Settings → Permissions, sets
|
||
Default View/Edit to the group `Household`. Otherwise new tags and
|
||
correspondents belong only to whoever created them, and the other person
|
||
doesn't see them, even on shared documents.
|
||
7. **Test:** one scan through each profile, then log in as `david` and as
|
||
`denise` and check who sees what.
|
||
|
||
### Double-sided documents
|
||
|
||
The MFC-L3750CDW's feeder scans **one side only**. Paperless merges two scans
|
||
into one document (`PAPERLESS_CONSUMER_ENABLE_COLLATE_DOUBLE_SIDED`):
|
||
|
||
1. Stack in the feeder with page 1 first, scan → pages 1, 3, 5.
|
||
2. **Turn the stack over** without re-sorting it, scan again → pages 6, 4, 2.
|
||
3. Paperless interleaves them into 1–6. The second scan must arrive
|
||
**within 30 minutes**, otherwise it counts as a new first half.
|
||
|
||
Needs a `double-sided` subfolder (Paperless doesn't create it):
|
||
`scans/David/double-sided`, `scans/Denise/double-sided`, and `scans/double-sided`
|
||
for shared documents. One printer profile each, e.g. `David 2-sided` →
|
||
`\\192.168.30.10\scans\David\double-sided`. Paperless treats
|
||
`David/double-sided/` like `David/`. `double-sided` doesn't become a tag.
|
||
**Still to test:** whether the owner workflow (`*/David/*`) also applies
|
||
to the merged document.
|
||
|
||
If the two scans get mixed up (a single-sided document scanned into the
|
||
`double-sided` profile), the next scan gets merged with the wrong one. So
|
||
use the double-sided profile only for genuinely double-sided documents.
|
||
|
||
The subfolders also become tags (`David`, `Denise`) through
|
||
`PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS`. That duplicates the owner, but is handy
|
||
for filtering. Drop the setting if it's in the way.
|
||
|
||
## Backups
|
||
|
||
Nightly export, as a crontab entry for `david` on the OptiPlex (`crontab -e`):
|
||
|
||
```
|
||
30 2 * * * cd ~/paperless && docker compose exec -T webserver document_exporter ../export --delete --no-progress-bar >> ~/paperless/export.log 2>&1
|
||
```
|
||
|
||
`--delete` removes files from the export that no longer exist in Paperless,
|
||
so the export mirrors the current state. Older versions are in Hyper Backup.
|
||
Later this moves into Ansible as a systemd timer.
|
||
|
||
- The export is **not encrypted**. It's on the NAS (Servers VLAN, share
|
||
only readable by `paperless` and admins), and Hyper Backup encrypts it on
|
||
the USB drive.
|
||
- **Test a restore** once: a throwaway instance, then
|
||
`document_importer ../export`.
|
||
- Off the OptiPlex, only `.env` matters. `./data` and `./media` are
|
||
covered by the export, and `./redisdata` is disposable.
|
||
|
||
## Notes
|
||
|
||
- Things to verify on first setup: after rebooting the OptiPlex while the NAS is
|
||
off, the container should start, and importing should resume on its own
|
||
once the NAS is back. If the mount doesn't show up inside the container,
|
||
check the `rslave` propagation in the compose file first.
|
||
- The image is pinned; check the release notes before bumping. 3.x removed
|
||
some database settings, which don't matter with SQLite.
|
||
- Office documents and emails need Tika + Gotenberg as extra containers.
|
||
Not needed for scans, so add them only if mail import becomes interesting.
|