Replaces the runOnServer("docker compose up -d --force-recreate") pattern with a one-shot transient docker:cli container that runs OUTSIDE kua-deploy lifecycle. Solves the self-recreate chicken-and-egg: when the target is kua-deploy itself, the recreate completes because the transient survives kua-deploy stopping (docker daemon does the actual work).
Secrets are fetched via kua-vault export, written to a 600-perm tempfile on /app/data, passed via --env-file (docker CLI reads it from kua-deploys perspective; never on the docker run command line). Tempfile is unlinked in finally{}.
Replaces: deploy() stateless recreate (force=true), deploy() stateful up (force=false), rollback() recreate (force=true with all-services svcList).
Build step keeps runOnServer (local exec on bruno) since build doesnt kill kua-deploy. envPrefix/kvPrefix vars retained for the build command.
Split rationale: kua-deploy used to be a service in coder-core/services/kua-services/docker-compose.yml, which meant every release-app coder-core rebuilt+force-recreated kua-deploy as a side-effect. The recreate-self path is structurally racy (the compose-up process is killed mid-flight when its own container stops), causing silent false-success deploys.
This split makes kua-deploy its own deploy unit (own repo, own compose project, own release-app entry), so coder-core releases no longer touch it. Phase A (transient-container recreateService pattern) will follow to make deliberate kua-deploy self-updates also reliable.
Handoff: v2-deploy-coordination -> kua-deploy-split (.sessions.md 2026-05-21 21:35).