feat(web-arm): install paperless-ngx #386
No reviewers
Labels
No labels
bug
enhancement
in-progress
needs-info
needs-triage
p0
ready-for-agent
ready-for-human
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/nixos!386
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/385"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Deploys paperless-ngx on web-arm as a native NixOS service at
https://paperless.cloonar.com,gated behind the existing Keycloak SSO, with its media tree on a dedicated Hetzner Storage-Box
CIFS share and a nightly restore-anywhere export feeding the existing borg job.
Closes #385
What lands
hosts/web-arm/modules/paperless/default.nix(new) —services.paperlesson the existingshared PostgreSQL 14 instance, a dedicated Redis on a unix socket, OCR in
deu+eng, one taskworker, the CIFS media mount, the nginx vhost, the nightly exporter, the borg exclusion and the
CPU throttling.
hosts/web-arm/configuration.nix— imports the new module.hosts/web-arm/modules/keycloak/{default.nix,cloonar-realm.json}— a confidentialpaperlessclient, a/paperlessgroup carrying apolicy-paperlessrealm role, and arole-gated browser flow mirroring the
browser-spnego immichfamily, plus the client-secretvault plumbing (
plainClients+keycloak-client-paperless).Design notes
Secret handling. The OIDC client secret never enters the Nix store. A sops template renders
PAPERLESS_SOCIALACCOUNT_PROVIDERSinto a systemdEnvironmentFile; rotation restarts the fourpaperless services via
restartUnits, and edits to the template body are picked up through arestartTriggershash of the placeholder-tokenised content — the same construct the Immich moduleuses. The JSON value is single-quoted deliberately: systemd's
EnvironmentFileparser stripsunescaped double quotes mid-value, which would shred the JSON, and
paperless-managesources thesame file from bash.
The CIFS mount is persistent, on purpose.
nofail,x-systemd.mount-timeout=2min, ownershipforced to
paperlessviauid=/gid=, andRequiresMountsForon every paperless unit thattouches media — upstream only sets it on
paperless-scheduler, so the task queue, consumer, weband exporter units get it here. There is deliberately no
x-systemd.automount/x-systemd.idle-timeoutanywhere: the idle unmount propagates a clean stop throughRequiresMountsFor, soRestart=on-failurenever fires — that is exactly what took Immich downfor 8–24h at a time.
tmpfiles modes are pinned. Upstream declares tmpfiles rules for the data, media and consume
directories with no explicit mode, i.e. 0755. Two consequences fixed here: the media rule is
pinned to 0770 so it matches what
dir_mode=0770makes the mounted share report — otherwise everybento rebuild re-runs tmpfiles against a live CIFS mount and the
chmodfails; and/var/lib/paperlessis pinned to 0750 so the consume directory and the nightly full-documentexport are not world-readable on a box that also hosts SFTP website users.
Backups. The media tree is excluded from borg (it lives on the same Storage Box as the borg
repo; Hetzner snapshots cover it). The restore-anywhere copy is the nightly
document_exporterrun into
/var/lib/paperless/export, which the job's existing/varpath already covers. It runsat 01:30, ahead of the 03:00 borg run, so each night's export ships the same night; the exporter
unit
Conflictsthe paperless services, so paperless is briefly down while it runs. The databasejoins the existing
postgresqlBackupdumps.Note the exclusion is declared in the paperless module rather than appended to
utils/modules/borgbackup.nixnext to the/var/lib/immich/entry. Immich's precedent points theother way, but a web-arm-only path in a module shared by eight hosts is the wart, and keeping it
local means the pre-commit hook dry-builds only the host actually affected. Say the word if you'd
rather it sat with the immich line and I'll move it.
Login. OIDC auto-signup is on, so
/paperlessmembers get an account on first login — but apermissionless one until the admin grants access; per-document ownership does the rest. Local
password login stays enabled for exactly one break-glass
adminaccount provisioned frompasswordFile, so a Keycloak outage does not lock the admin out.Before this can deploy — maintainer actions
Secrets to add to
hosts/web-arm/secrets.sops.yamlnix-shell -p sops --run 'sops hosts/web-arm/secrets.sops.yaml'paperless-oidc-client-secretpaperlessclient secret from Keycloak. Read twice: by the paperless module (into the rendered env file) and by the Keycloak module askeycloak-client-paperless(into the file vault ascloonar_paperlesssecret).paperless-smb-credentialsmount.cifsformat: ausername=line and apassword=line.paperless-admin-passwordadminaccount.Until these exist the host still evaluates, but activation fails — sops resolves secrets at
deploy time, not at eval time.
External actions
u149513-sub13(the module mounts//u149513.your-backup.de/u149513-sub13/), enable Samba/CIFS on it, and turn on the snapshotschedule.
sub13was picked as the next unreferenced number in this repo; if it is alreadytaken on the Hetzner side, change the
deviceline to the sub-account you create.Arecord forpaperless.cloonar.compointing at web-arm. The certificate thenissues itself through the existing lego DNS-01 setup; no repo change needed for that.
scan@cloonar.comon the mail host for scan-to-email(
scan+<user>@cloonar.complus-addressing)./paperlessgroup. Nobody can complete a loginuntil they hold
policy-paperless./var/lib/paperless/exportis a full copy of every document, on the localSSD and inside the borg set. It is the one place the media-on-Storage-Box split does not save
local space; worth sizing before the archive grows.
Runtime configuration (deliberately not in this PR)
The IMAP account, per-user mail rules and owner-assigning workflows, and per-user permissions are
configured in the paperless UI after first login.
Not included, possibly wanted
ansible/roles/ipa_structure/defaults/main.ymlhas ansvc-immichaccess group but gained nosvc-paperlesshere — the issue scoped LDAP/group work as a human action. Happy to add thethree-line entry if it belongs in this PR.
Testing
This repo has no unit tests; the gate is the pre-commit hook's dry-build, which ran on
web-armand passed. Beyond that I evaluated the resulting config and checked the things that are easy to
get quietly wrong:
PAPERLESS_URLresolves tohttps://paperless.cloonar.com,PAPERLESS_REDISresolves to the module's own
unix:///run/redis-paperless/redis.sock,PAPERLESS_DBHOSTis the/run/postgresqlsocket, OCR isdeu+eng,PAPERLESS_TASK_WORKERSis 1, tika and gotenberg areboth off,
CPUWeightis 20 on the scheduler/task-queue/consumer/exporter and untouched on the webunit, the four non-scheduler units carry
RequiresMountsFor, the mount options contain noautomount/idle-timeout, and the rendered template is single-line single-quoted JSON carrying only
a sops placeholder. The Keycloak realm JSON was checked for alias/name uniqueness and for every
flowAlias,authenticatorConfig,condUserRoleand browser-flow override resolving.Runtime verification after deploy is yours.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.