paperless-ngx 3 rebuild — reset & cutover (#2106)¶
Runtime steps for the operator, around the merge of the rebuild PR
(branch feat/paperless3-rebuild). Design + decisions:
.workingdir2/research/31-paperless3-rebuild/{02-design.md,99-decisions-for-operator.md}.
The app is verified ~empty; the reset is pre-approved (D4/D5).
What the PR changes: all four PVCs become explicit manifests —
data → paperless-data-office (longhorn-office block), media/consume/export
→ static NFS PVs at fixed NAS paths pre-bound via volumeName; v3 env
surface (trusted proxies, archive generation, stability delay, duplicate
rejection, filename format); nightly document_exporter CronJob to the
/volume4 backup share; office PVC quota 12 → 16 (swap overlap).
Prerequisites¶
- Workstation in the office zone (or cluster API via cloudflared).
- SSH access to the office Synology (
192.168.2.19). kubectlread + exec against the cluster; ArgoCD UI/CLI access.
1. NAS prep (BEFORE merge — pods cannot mount missing NFS subdirs)¶
ssh <admin>@192.168.2.19
mkdir -p /volume7/k8s-apps/paperless/media /volume7/k8s-apps/paperless/consume
mkdir -p /volume4/k8s-backups/paperless/export
chown -R 1000:1000 /volume7/k8s-apps/paperless /volume4/k8s-backups/paperless
Operator TODO (NAS-side, D5, #2106): create the SMB shared folder for the
scanner on /volume7/k8s-apps/paperless/consume in DSM (Control Panel →
Shared Folder → point at that path; give the scanner account write
access). Needed before the scanner workflow works; not needed for the
cutover itself. Subdirs the scanner creates become paperless tags.
2. Safety dump (BEFORE the DB reset — even though the app is ~empty)¶
PRIMARY=$(kubectl get cluster -n databases pg-cluster -o jsonpath='{.status.currentPrimary}')
kubectl exec -n databases "$PRIMARY" -c postgres -- \
pg_dump -U postgres --clean --if-exists -d paperless \
> paperless-pre-rebuild-$(date +%F).sql
3. DB reset to a clean schema (v3 migrates into it on first boot)¶
Keeps the paperless database itself (the CNPG Database CR keeps
reconciling its existence); only the contents reset. The running old pod
will error from here until the roll — expected, this is the window.
kubectl exec -n databases "$PRIMARY" -c postgres -- psql -U postgres -d paperless -c \
'DROP SCHEMA public CASCADE; CREATE SCHEMA public AUTHORIZATION paperless; GRANT ALL ON SCHEMA public TO paperless;'
4. Merge the PR¶
Auto-sync does the rest in one sync: static PVs + new PVCs created and pre-bound, deployment rolls onto the new volumes, exporter CronJob created, old chart-managed PVCs pruned (deletion completes once the old pod terminates and releases them). No manual scale-down needed — the volume swap forces the roll, and app-of-apps selfHeal would revert a manual sync-policy pause anyway.
5. Readback checks (in order)¶
# PVCs: 4 new Bound, old 4 gone
kubectl get pvc -n office | grep paperless
# static PVs bound to the right claims
kubectl get pv paperless-media-office paperless-consume-office paperless-export-office
# pod up, migrations clean, consumer POLLING (not inotify), no deprecation warnings
kubectl logs -n office deploy/paperless-ngx-paperless | grep -iE 'polling|migrat|warn|error' | head -50
Then functionally:
- Login via
https://docs.office.cauda.dev(Authentik) works. - Upload a test PDF in the UI → consumed, OCR runs, AI suggestions arrive in German (B60 cold start can take minutes on first call).
- Scanner end-to-end: drop a PDF on the consume SMB share → picked up within ~75 s (60 s poll + 15 s stability), subdir becomes a tag.
- Exporter first run — trigger manually instead of waiting for 01:30:
kubectl create job -n office --from=cronjob/paperless-ngx-exporter paperless-export-manual
kubectl logs -n office job/paperless-export-manual -f
ssh <admin>@192.168.2.19 ls -la /volume4/k8s-backups/paperless/export # manifest.json + version, non-empty
6. Cleanup¶
# old dynamic PVs are Released (SC reclaim=Retain) — remove the objects;
# file data stays in the old pvc-UUID subdirs on the NAS
kubectl get pv | grep Released | grep office
kubectl delete pv <the four released pvc-… volumes>
After a verified week: remove the old
/volume7/k8s-apps/office-paperless-ngx-*-pvc-* subdirs on the NAS.
Append the cutover result to STATE.md.
What can go wrong¶
- New pod Pending / PVC mount failure — NAS subdir missing (step 1)
or a static PV/PVC pre-bind mismatch (
volumeName↔claimRefnames). - PVCs denied by quota — mitigated by the 12→16 bump in the PR; check
kubectl describe resourcequota -n office. - Old PVCs stuck Terminating — old pod still holds them; check the rollout. Last resort is the finalizer-patch pattern from the /volume7 migration — only after confirming nothing mounts them.
- Migrations fail on first boot — schema not clean; re-run step 3, then delete the pod.
- Exporter PermissionError on export dir — ownership must be 1000:1000 (step 1).
- Later: v3.0.0 stable re-pin — bump the digest in
apps/office/paperless-ngx/values.yaml(single anchor covers deployment AND CronJob); the next nightly export re-runs against the new version. Exports are version-locked — never restore an old export into a newer paperless.