Files
serverAppConfigs/serverAppConfigs-docs/Architecture/Storage Policy.md
T
Frank 272ef6f0b0 docs: record quadlet standardization of dankserver media + dankclaw data roots
Phase 1/2 of the podman standardization plan: service pages updated to the
quadlet reality (units, Containers data roots, env-file secret convention,
the v1-generator findings each caught live), Storage Policy documents the
two rootless ownership regimes and the merge-move migration, Deployment
Flow's service-manager table no longer claims dankserver media is bare
inventory-only. Bundle change log entry added.
2026-09-01 13:57:33 -07:00

2.6 KiB

Storage Policy

All persistent data uses bind mounts: a host directory mapped directly into the container. No named volumes. This matches the preference of this repo and the existing Gitea stack on Dankclaw.

Why Bind Mounts

  • The exact host location of every byte is visible and controllable.
  • Backups are a simple tar of known host directories.
  • The data survives container recreation and podman-compose down.
  • Ownership is explicit: everything runs as UID 1000 (dank) so host tools can read it.

Conventions

Stack Host data root
Dankserver /home/dank/Containers/<svc>/ (romm, jellyfin, calibre-web, copyparty)
Dankclaw /home/dank/Containers/<svc>/ (caddy, gitea, authentik, hermes, …)

This layout was standardized on 2026-09-01. The old /opt/<service> roots were migrated by the deployers' migrate_data_root (merge-move, numeric ownership preserved); /opt holds no live container data on either host.

Ownership

Containers are rootless. Two regimes exist and the deployer must respect both:

  • PUID/PGID images (linuxserver calibre-web, jellyfin): data is owned by host uid 1000 (dank) — plain chown is safe.
  • Mapped-uid images (official mariadb, redis, authentik, postgres): files land in the subuid range (e.g. 100998 for container uid 999). Renaming such a directory requires root (rename needs write on the directory itself) and re-chowning to 1000 breaks the container's internal user. Migrations therefore use sudo mv (numeric-preserving) and skip the chown pass (migrate_data_root … keep).
stat -c '%u %n' /home/dank/Containers/romm/mariadb   # 100998 = mapped, leave it

Secrets

Secrets never live in bind-mounted configs that get committed. The quadlet convention on both hosts:

  1. One host-local env file per stack at /home/dank/Containers/<svc>/env (mode 0600), referenced from the unit's [Service] EnvironmentFile= so ${VAR} expansion works at ExecStart time; never committed.
  2. .env files in the checkout (git-ignored, .env.example committed as template) for compose-era and Caddy-rendered secrets.

Deployed units hold only variable expansions (${COPAY_ACCOUNT} …), never literals — enforced by the no secret material seams. The live Gitea database password exists only in /home/dank/Containers/gitea/env on Dankclaw. A test enforces that it never appears in this repo.

See Also