5.0 KiB
type, title, description, tags, timestamp
| type | title | description | tags | timestamp | |||
|---|---|---|---|---|---|---|---|
| guide | Deployment Flow | Service-scoped Git deployment for the two homelab hosts. |
|
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]
- Edit files locally in this repository.
- Run
./tests/run.sh, then commit the complete change onmain. - Run
./scripts/deploy.sh <target>. - 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.
- The target pulls with
git pull --ff-only origin mainand runs the remote dispatcher with the explicit plan. - 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 dankclaw --service searxng
./scripts/deploy.sh dankserver --service frank-default-small
./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 | user Quadlet + systemctl --user |
Installs the selected unit and restarts only the required dependency-coupled units. |
frank-default-large |
systemd + systemctl |
Relinks and restarts only the large inference service; waits up to 20 minutes for /health. |
frank-default-small |
systemd + systemctl |
Relinks and restarts only the small inference service; waits up to 20 minutes for /health. |
| Dankserver media inventory | host-managed bare containers | Compose is inventory only; the scoped deploy does not create duplicate containers. |
The shared chat template maps to the inference service on the selected target.
The inactive frank-default-npu unit is relink-only when explicitly selected;
it is not restarted by an unrelated large-model deployment.
Caddy safety
Caddy runs on Dankclaw and fronts both hosts. The dispatcher never stops
gitea-caddy implicitly. If the legacy gitea-caddy container owns ports
80/443 while the compose caddy container is absent or stopped, a Caddy
selection fails closed rather than replacing the live proxy. Resolve port
ownership explicitly before retrying. A Caddy reload failure falls back to a
restart of only the selected caddy container.
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.