--- type: reference description: "All persistent data uses **bind mounts**: a host directory mapped directly into the container." tags: - architecture - storage timestamp: 2026-09-01 --- # 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//` (romm, jellyfin, calibre-web, copyparty) | | Dankclaw | `/home/dank/Containers//` (caddy, gitea, authentik, hermes, …) | This layout was standardized on 2026-09-01. The old `/opt/` 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`). ```bash 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//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 - [[Deployment Flow]] - [[Gitea]] — the original bind-mount stack, now on the Containers layout - [[Backblaze Integration]] — future backup target for these mounts