Infrastructure/docker/paperless
2026-10-05 22:14:45 +02:00
..
.env.example Adds paperless setup 2026-10-05 22:14:45 +02:00
compose.yaml Adds paperless setup 2026-10-05 22:14:45 +02:00
README.md Adds paperless setup 2026-10-05 22:14:45 +02:00

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:

    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.