comfyui: replace the stale compose container with a Quadlet unit

ComfyUI had failed 9,146 times and had been down ~7 hours when this was found:
its container was created 2026-09-15 14:12, BEFORE the GPU declarations were
added to the compose service, and `restart: unless-stopped` kept restarting
that stale container — no /dev/kfd, no /dev/dri, no keep-groups — so it died
every time on `RuntimeError: No HIP GPUs are available`. Its "unit" was a
hand-written ~/.config/systemd/user/comfyui.service that shelled out to
`podman compose up -d`, which is not repo-tracked state and collides by name
with the Quadlet the generator produces.

  - dankclaw/comfyui/quadlet/comfyui.container: image comfyui-rocm:gfx1151
    (built from Containerfile.comfyui), /dev/kfd + /dev/dri, keep-groups,
    the four comfyui-data volumes, loopback-only publish (an unguarded service
    is internal-only here; the homepage card probes 127.0.0.1:8188), Restart=
    always, WantedBy=default.target.
  - the compose service is deleted — one owner per container name.
  - deploy_comfyui builds the image, installs the unit, retires the compose-era
    unit into ~/.AGENTTRASH, removes the stale container so the unit's own can
    take the name, and waits on :8188.
  - the healthcheck is a file, because the podman 5.x v1 generator splits
    HealthCmd on spaces and an inline `python3 -c "..."` becomes a quoting
    puzzle that fails silently.

The comfyui seam now reads the unit instead of compose for devices, command and
groups (and resolves the Containerfile from the deployer's declaration, not
from a directory listing).
This commit is contained in:
Frank
2026-09-16 07:13:27 -07:00
parent d8835e69c6
commit 03d44d3308
5 changed files with 180 additions and 104 deletions
+29 -6
View File
@@ -383,12 +383,7 @@ deploy_compose_service() {
return 0
fi
case "$service" in
comfyui|hermes|hermes-dashboard|hermes-webui)
case "$service" in
comfyui)
build_compose_image "$service"
;;
esac
hermes|hermes-dashboard|hermes-webui)
podman-compose -f dankclaw/compose.yaml up -d --no-deps "$service"
;;
*)
@@ -572,6 +567,33 @@ deploy_open_webui() {
systemctl --user restart open-webui.service
}
deploy_comfyui() {
# Build first: the unit's Image= resolves against local storage, so the
# image has to exist before the generator starts a container from it.
build_compose_image comfyui
install_quadlet comfyui dankclaw/comfyui/quadlet comfyui
# Retire the compose-era unit. It owns the same unit name the Quadlet
# generator produces, and it is what kept a stale, device-less container
# alive through `restart: unless-stopped` (9,146 failures on
# "No HIP GPUs are available"). Never `rm`: it moves into the home trash.
local legacy="$HOME/.config/systemd/user/comfyui.service"
if [[ -e "$legacy" ]]; then
local trash="$HOME/.AGENTTRASH/20260916-comfyui-compose-unit"
mkdir -p "$trash"
systemctl --user stop comfyui.service 2>/dev/null || true
systemctl --user disable comfyui.service 2>/dev/null || true
mv -- "$legacy" "$trash/comfyui.service"
echo "retired the compose-era comfyui.service -> $trash"
fi
# The stale container carries the old config (no /dev/kfd, no /dev/dri) and
# would keep the quadlet's container from taking the name. It is replaced
# by the unit in the next step, so nothing is lost.
podman rm -f comfyui >/dev/null 2>&1 || true
systemctl --user daemon-reload
systemctl --user restart comfyui.service
wait_for_health comfyui http://127.0.0.1:8188/ 300
}
deploy_pihole() {
# Rootless port 53 needs net.ipv4.ip_unprivileged_port_start <= 53, and it
# must hold at boot. The repo drop-in is the single source of truth.
@@ -1237,6 +1259,7 @@ deploy_service() {
plex) deploy_plex ;;
tautulli) deploy_tautulli ;;
prometheus|grafana|loki|alloy|node-exporter) deploy_observability "$service" ;;
comfyui) deploy_comfyui ;;
*) deploy_compose_service "$service" ;;
esac
}