Files
serverAppConfigs/serverAppConfigs-docs/Architecture/Deployment Flow.md
Frank a5fe045b58 docs(vault): one fact, one owner — repair the doubled pages, single-source the facts
Structural repair:
- 13 pages were the same document twice (each exactly 2x its HEAD size),
  a second frontmatter block whose closing fence had been glued onto a body
  line by a frontmatter rewrite that re-read its own output. Un-doubled by
  cutting at the second frontmatter and keeping the copy whose body is a
  superset; the orphan census reports 0 lost tokens against HEAD.
- log.md: one entry per date, bare `## YYYY-MM-DD` headings, newest first —
  66 dated headings merged into 34, titles demoted to bold leads, every
  content line verified present. The file had been descending at the head
  and ascending at the tail.

Ownership moves:
- New `LLM/frank-medium.md` is the locator for the retired Dankserver
  endpoint: era windows quoted from each era page's own banner, no numbers
  restated, and the two unresolved date conflicts recorded instead of
  silently resolved. `frank-archon.md` links to it instead of restating the
  pre-rename window.
- The four Pipeline-Parallelism stubs (frontmatter only, no body) are gone;
  `Multi-Node Runtimes.md` owns runtime construction, launch, mixed-vendor
  verdicts and cross-split spec decode, and the four links that pointed at
  the stubs were repointed at it or at the drafter page.
- Authentik's three stub pages fold into `Authentik.md`, with the process
  detail and the outpost reference split out so the parent stays under the
  length hint.

Verified corrections (host census + live probe, 2026-10-19):
- The MXFP4 checkpoint two pages called the "on-disk rollback" is gone:
  /home/dank/models/ holds StillDeadcode, rad and tpl.jinja. Stated once, on
  the era page that owns that payload; the swap page now links to it.
- frank-medium-radiance-mxfp4: the 4.24/7 accept rate belongs to the DFlash2
  arm measured on the FP8 target, not to the MXFP4 era; the 3-family soak run
  census is kept once (155 requests) instead of twice (~120 and 155).
- Fleet shapes re-confirmed against the live endpoints before touching any
  page: 3x200000 fp8 KV, 2x200704, 2x262144.

Index rows are regenerated, not hand-carved. Net -1,849 vault lines.
2026-10-19 05:00:43 -07:00

5.7 KiB

type, title, description, tags, timestamp
type title description tags timestamp
howto Deployment Flow Service-scoped Git deployment for the two homelab hosts.
deployment
operations
git
2026-09-03

Deployment Flow

Git is the single source of truth. scripts/deploy.sh pushes the committed main branch, pulls it on one target, and applies only the runtime services whose tracked inputs changed. A SearXNG change no longer recreates the inference service; an LLM change no longer restarts unrelated containers.

Workflow

graph LR
    A[Edit in this repo] --> B[Run tests]
    B --> C[Commit main]
    C --> D[deploy.sh computes plan]
    D --> E[Push main]
    E --> F[Target pulls with ff-only]
    F --> G[deploy-remote.sh dispatches selected services]
    G --> H[Per-service health check]
  1. Edit files locally in this repository.
  2. Run ./tests/run.sh, then commit the complete change on main.
  3. Run ./scripts/deploy.sh <target>.
  4. The planner maps changed paths to services. Compose YAML is compared by service definition so an edit to one service does not restart its peers.
  5. The target pulls with git pull --ff-only origin main and runs the remote dispatcher with the explicit plan.
  6. The dispatcher updates only selected Quadlet, compose, homepage, Caddy, or systemd services and records each successful service revision outside Git.

Commands

./scripts/deploy.sh dankclaw
./scripts/deploy.sh dankserver
./scripts/deploy.sh frankbook-pro --service qbittorrent   # podman-machine target
./scripts/deploy.sh dankclaw --service searxng
./scripts/deploy.sh dankserver --service frank-archon
./scripts/deploy.sh dankclaw --dry-run

--service is repeatable and is an explicit retry/override. Without it, the planner compares the local HEAD to origin/main. A documentation-only commit produces an empty plan. The remote state directory can also replay a service whose earlier deployment failed, but it never expands a plan to a service on the other host.

Service managers

Service family Runtime manager Scope behavior
Dankclaw compose services podman-compose -f dankclaw/compose.yaml up -d --no-deps NAME Recreates only NAME; Caddy is handled separately because it fronts both hosts.
SearXNG, Gluetun, Valkey, OpenWebUI, Pi-hole, Gitea database/server user Quadlet + systemctl --user Installs the selected unit and restarts only the required dependency-coupled units. Gitea's web proxy is the canonical Caddy compose service.
frank-dragoon systemd + systemctl (root unit) Relinks and restarts only the Dankclaw iGPU inference service; waits up to 20 minutes for /health. Shape, gates and boot budget: frank-dragoon.
frank-zealot systemd + systemctl (root unit) Relinks and restarts only the Dankclaw 3090 inference service; waits up to 20 minutes for /health. Shape, gates and boot budget: frank-zealot.
frank-archon systemd + systemctl (root unit) deploy_llm_archon plans from repo paths and gates on the port; it does NOT see host-side unit drift (see frank-archon → Deploy / verify).
Dankserver media stack (RomM trio, Jellyfin, Calibre-Web, Copyparty) user Quadlet + systemctl --user Installs dankserver/<svc>/quadlet/*.container, restarts the generated unit. The first cutover per service is idempotent: it retires the compose-era bare container, rescues any anonymous volume into the data root, and merge-moves /opt/<svc> to /home/dank/Containers/<svc> (mapped-uid roots move without chown). Steady state is restart-only.

The shared chat template maps to the inference service on the selected target. The frank-npu endpoint is retired (frank-npu) and has no deploy handler; an unrelated large-model deployment never touches it.

Caddy safety

Caddy runs on Dankclaw and fronts both hosts, including Gitea. The former standalone gitea-caddy Quadlet is retired; the canonical compose caddy service is now the sole reverse-proxy owner of ports 80/443. A Caddy reload failure falls back to a restart of only the selected caddy container. Before deploying, verify that no retired proxy still owns either port.

Homepage files, generated deployed.js, the status checker, and the rendered Caddyfile are updated only when their service is selected. A media-only Dankserver deployment does not touch Dankclaw's proxy or homepage.

Per-service state

The remote runner writes one revision file per successfully completed service to:

${XDG_STATE_HOME:-~/.local/state}/serverAppConfigs/deploy-state/

The directory is outside the checkout and is never committed. A failed handler is not marked successful. On the next ordinary deployment, the runner compares the recorded revision with the new checkout and retries only a still-changed service. This prevents a failed Caddy or LLM deployment from being silently forgotten while avoiding a host-wide restart.

Targets

Target Host Repository checkout
dankclaw dank@dankclaw (192.168.0.143) /home/dank/serverAppConfigs
dankserver dank@dankserver (192.168.0.235) /home/dank/serverAppConfigs

SSH uses the existing key pair. No password is stored or documented in this repository. If sudo is needed on a headless target, FRANK_SUDO_PW is read from the environment and is never written to the repository.

Tests

Run the full suite before pushing:

./tests/run.sh

The deployment seams are in tests/deployment_scoping.bats and tests/deployment_flow.bats. They cover path isolation, compose narrowing, Caddy safety, inference isolation, per-service state, and legacy wrapper routing. See Testing Policy.

See Also