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.
This commit is contained in:
Frank
2026-09-01 13:57:33 -07:00
parent 34ab6b4d0c
commit 272ef6f0b0
7 changed files with 150 additions and 40 deletions
@@ -49,12 +49,20 @@ Additional packages added: `libxfixes3` and `libasound2t64` to support epub-to-p
## Volumes
| Container path | Host path (convention) |
|----------------|------------------------|
| `/config` | `/opt/calibre-web/config` |
| `/books` | The existing Calibre library directory (user-entered during setup) |
| Container path | Host path |
|----------------|-----------|
| `/config` | `/home/dank/Containers/calibre-web/config` |
| `/books` | `/home/dank/Containers/calibre-web/books` |
## Docker Compose
Deployment since 2026-09-01 is a quadlet unit
(`dankserver/calibre-web/quadlet/calibre-web.container` →
`calibre-web.service`, deploy with
`./scripts/deploy.sh dankserver --service calibre-web`), image pinned to
`0.6.27-ls397`, healthcheck on `/`, `PublishPort=8083:8083`. This service was
already on the Containers data-root layout before the standardization, so the
cutover was a bare-container retire + unit start with no data movement.
## Docker Compose (reference — retired 2026-09-01; live mounts are under `/home/dank/Containers/calibre-web`)
```yaml
services:
+19 -11
View File
@@ -80,7 +80,7 @@ takes its volume roots from argv plus `chdir: /w` in `/z/initcfg`, not from a
rw on disk — copyparty enforces read-only at the application layer, so local
services (Jellyfin, Calibre) keep writing into the array directly.
## Docker Compose
## Docker Compose (reference — retired 2026-09-01; live deployment is the quadlet unit below)
```yaml
copyparty:
@@ -100,20 +100,28 @@ services (Jellyfin, Calibre) keep writing into the array directly.
retries: 3
```
`${COPAY_PASS}` is compose-time interpolation from `dankserver/.env` — the
password lands in the container's **argv**, visible to anyone who can run
`${COPAY_ACCOUNT}`/`${COPAY_PASSWORD}` are **never** written into the unit:
they live in the host-local `/home/dank/Containers/copyparty/env` (0600),
loaded via `[Service] EnvironmentFile=` so systemd expands them into
`Exec=-a ${COPAY_ACCOUNT}:${COPAY_PASSWORD}` at start. The password still
lands in the container argv — visible to anyone who can run
`podman inspect copyparty` on the host.
**Applying changes:** `scripts/deploy.sh` deliberately does not touch the
dankserver compose stack (host containers are not quadlet-managed there), so
`command:` edits need a manual recreate on Dankserver:
## Deployment (Quadlet)
```
cd /home/dank/serverAppConfigs/dankserver && podman-compose up -d --force-recreate copyparty
```
Since 2026-09-01: `dankserver/copyparty/quadlet/copyparty.container` →
`copyparty.service`, deployed with
`./scripts/deploy.sh dankserver --service copyparty`. Cutover details:
Same project (`dankserver`), only copyparty is recreated; a fresh container
answers `000` for the first seconds while it mounts.
- v1 generator rejects `Command=` → the argv lives in `Exec=`.
- No `HealthCmd=` — the v1 generator splits unquoted spaces in that key;
self-healing is `Restart=always`.
- `PublishPort=3923:3923` — the first cutover dropped it (unit joined
`dankserver_appnet`), copyparty answered inside the netns only; the
published-port seam test now pins 3923 alongside 8085/8096/8083.
- Config root moved `/opt/copyparty` → `/home/dank/Containers/copyparty/cfg`.
- The deployer refuses to start the unit before the env file exists, so a
missing seed cannot create an admin account with a garbage password.
## Healthcheck
+16 -4
View File
@@ -31,7 +31,7 @@ graph LR
J --> Cache[Jellyfin cache]
```
## Docker Compose
## Docker Compose (reference — retired 2026-09-01; live mounts use `/home/dank/Containers/jellyfin`, see Deployment below)
```yaml
services:
@@ -82,11 +82,23 @@ Set **Base URL** to `/` (or the path you configure) in Jellyfin Dashboard → Ne
| Mount | Purpose | Size Estimate |
|-------|---------|---------------|
| `/opt/jellyfin/config` | SQLite database, settings, cached images | 10–100 GB |
| `/opt/jellyfin/cache` | Transcoding segments, extracted posters | Self-cleaning per session |
| `/home/dank/Containers/jellyfin/config` | SQLite database, settings, cached images | 10–100 GB |
| `/home/dank/Containers/jellyfin/cache` | Transcoding segments, extracted posters | Self-cleaning per session |
| `/mnt/entropy/Movies`, `/mnt/entropy/TVShows` | Media, **read-only binds** (`:ro`) — no automated write path | — |
The database must be on local storage (SSD/NVMe preferred). Media libraries can be on NFS/mergerfs. If media is on NFS, set `DOTNET_SYSTEM_IO_DISABLEFILELOCKING=true`.
## Deployment (Quadlet)
Since 2026-09-01: `dankserver/jellyfin/quadlet/jellyfin.container` →
`jellyfin.service` (generator-enabled), deployed with
`./scripts/deploy.sh dankserver --service jellyfin`. GPU passthrough uses
`AddDevice=/dev/dri` — plain `Device=` is **not** a quadlet key and makes the
v1 generator reject the whole unit (learned live: "unsupported key 'Device'
in group 'Container'"). Entropy mounts stay `:ro`; the container runs as
root inside the user namespace, so the render-node ACLs apply via the device
cgroup regardless of `GroupAdd=keep-groups`.
## Authentication
- **Built-in auth (default):** Per-user libraries, parental controls, max bitrate limits. Manage via Dashboard → Users.
@@ -147,7 +159,7 @@ Run Jellyfin alongside Plex for an incremental migration. Point Jellyfin at the
1. Verify GPU on host: `ls -l /dev/dri/`, `lspci | grep -i vga`, `getent group render`.
2. Install media drivers: `sudo apt install intel-media-va-driver-non-free vainfo intel-gpu-tools`.
3. Create directories: `/opt/jellyfin/config`, `/opt/jellyfin/cache`.
3. Create directories: `/home/dank/Containers/jellyfin/config`, `/home/dank/Containers/jellyfin/cache`.
4. Write compose file, verify device and GID.
5. Deploy: `podman compose up -d`.
6. Test GPU passthrough: `podman exec jellyfin /usr/lib/jellyfin-ffmpeg/vainfo --display drm --device /dev/dri/renderD128`.
+31 -8
View File
@@ -73,7 +73,7 @@ These are set via environment variables or in `config.yml`:
### Config File
RomM uses `config.yml` for scan settings, available through the admin UI (Scan Settings). Place the file at `/opt/romm/config/config.yml` on the host.
RomM uses `config.yml` for scan settings, available through the admin UI (Scan Settings). Place the file at `/home/dank/Containers/romm/config/config.yml` on the host.
Key scan config options:
@@ -99,17 +99,40 @@ scan:
export: true # Export ES-DE compatible gamelist.xml
```
## Deployment (Quadlet)
Since 2026-09-01 the trio runs as repo-tracked quadlet units, deployed with
`./scripts/deploy.sh dankserver --service romm` (any of `romm`,
`romm-mariadb`, `romm-redis` triggers the full ordered cutover):
- Units: `dankserver/romm/quadlet/{romm,romm-mariadb,romm-redis}.container`
→ generated `romm.service` etc., enabled via the quadlet generator's
`default.target.wants` (`is-enabled` reports `generated`).
- Secrets: host-local `/home/dank/Containers/romm/env` (0600, 15 app vars —
compose-provided keys only; image-baked vars like `HOME`/`PATH` must never
enter it, they poison the podman client's service environment).
- Network: `dankserver_appnet` with aliases `mariadb`/`redis` (replaces
compose service discovery; `DB_HOST`/`REDIS_HOST` still resolve).
- Publish: `8085:8080` (`PublishPort=`, see the seam test on published ports).
## Volumes
| Container path | Host path (convention) |
|----------------|------------------------|
| `/romm/library` | `/opt/romm/library` |
| `/romm/resources` | `/opt/romm/resources` |
| `/romm/config` | `/opt/romm/config` |
| Container path | Host path |
|----------------|-----------|
| `/romm/library` | `/home/dank/Containers/romm/library` |
| `/romm/resources` | `/home/dank/Containers/romm/resources` |
| `/romm/config` | `/home/dank/Containers/romm/config` |
| `/var/lib/mysql` | `/home/dank/Containers/romm/mariadb` (mapped subuid owner — never chown) |
| `/data` (redis) | `/home/dank/Containers/romm/redis` (mapped subuid owner) |
The library volume contains ROM files and subdirectories per platform. The resources volume stores cached artwork, metadata, and the SQLite/SQLite-backed asset cache. The config volume holds `config.yml` and other configuration.
The library volume contains ROM files and subdirectories per platform. The resources volume stores cached artwork, metadata, and the asset cache. The config volume holds `config.yml` and other configuration.
## Docker Compose
The cutover rescued RomM's anonymous `/romm` volume (assets, plus empty
mountpoint stubs that shadowed nothing) into the data root before first unit
start — the mechanism behind the original "RomM data wiped" incident is now
structurally impossible: the units declare bind mounts only.
## Docker Compose (reference — retired 2026-09-01, superseded by the quadlet units above)
```yaml
services: