agent-proxy role never rebuilds the image on converge (docker compose up --force-recreate, no --build/--pull) - upstream fixes never reach the host #431

Closed
opened 2026-07-02 07:59:54 +00:00 by coilysiren · 2 comments
Owner

Problem

ansible/roles/agent-proxy/tasks/main.yml brings the container up with:

- name: Bring up agent-proxy with the loopback port bound
  ansible.builtin.shell: docker compose up -d --force-recreate
  when: agent_proxy_compose.changed or (agent_proxy_running.stdout | trim) == ''

docker compose up builds the image only when it is missing, and --force-recreate recreates the container from the existing image - it does not rebuild the image or re-pull the git build context. The compose file builds from https://forgejo.coilysiren.me/coilyco-flight-deck/agent-proxy.git#main and tags agent-proxy-local:latest.

So once agent-proxy-local:latest exists on a host, every later ward exec ansible-sync reuses that cached image. A fix landed on agent-proxy main never reaches the host, because the role never rebuilds. This is how a crash-looping image (see the agent-proxy dependency bug) stays crash-looping across syncs.

Fix

Make the converge rebuild from the current upstream ref. Either:

  • docker compose up -d --build --pull always --force-recreate, or
  • an explicit docker compose build --pull step before up.

Pick the form that fits the house style of the sibling dockerized roles (mac-proxy, dozzle, logdy). Keep the loopback bind and the existing when: gating. Ensure a genuine upstream change on agent-proxy#main is picked up on the next sync, and that an unchanged ref does not thrash (docker layer cache handles the no-op case).

Acceptance

  • After a fix lands on agent-proxy#main, a single ward exec ansible-sync rebuilds and runs the new image without a manual docker compose build.
  • A no-op sync (no upstream change) does not force a full rebuild every run.

Refs

  • Role added in infrastructure#425. Same role/file as infrastructure#430 (host-port mapping) - integrate that change when it lands.
## Problem `ansible/roles/agent-proxy/tasks/main.yml` brings the container up with: ```yaml - name: Bring up agent-proxy with the loopback port bound ansible.builtin.shell: docker compose up -d --force-recreate when: agent_proxy_compose.changed or (agent_proxy_running.stdout | trim) == '' ``` `docker compose up` builds the image only when it is **missing**, and `--force-recreate` recreates the *container* from the existing image - it does not rebuild the image or re-pull the git build context. The compose file builds from `https://forgejo.coilysiren.me/coilyco-flight-deck/agent-proxy.git#main` and tags `agent-proxy-local:latest`. So once `agent-proxy-local:latest` exists on a host, every later `ward exec ansible-sync` reuses that cached image. A fix landed on `agent-proxy` `main` never reaches the host, because the role never rebuilds. This is how a crash-looping image (see the agent-proxy dependency bug) stays crash-looping across syncs. ## Fix Make the converge rebuild from the current upstream ref. Either: - `docker compose up -d --build --pull always --force-recreate`, or - an explicit `docker compose build --pull` step before `up`. Pick the form that fits the house style of the sibling dockerized roles (mac-proxy, dozzle, logdy). Keep the loopback bind and the existing `when:` gating. Ensure a genuine upstream change on `agent-proxy#main` is picked up on the next sync, and that an unchanged ref does not thrash (docker layer cache handles the no-op case). ## Acceptance - After a fix lands on `agent-proxy#main`, a single `ward exec ansible-sync` rebuilds and runs the new image without a manual `docker compose build`. - A no-op sync (no upstream change) does not force a full rebuild every run. ## Refs - Role added in infrastructure#425. Same role/file as infrastructure#430 (host-port mapping) - integrate that change when it lands.
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-infrastructure-431 on host kais-macbook-pro-2.local is carrying this issue (reserved 2026-07-02T08:00:18Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-infrastructure-431` on host `kais-macbook-pro-2.local` is carrying this issue (reserved 2026-07-02T08:00:18Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - agent-proxy converge now runs docker compose up -d --build --pull always --force-recreate, so upstream agent-proxy#main fixes rebuild onto the host.

Small, clean change - the actual fix was two words on one line. Most of the thinking was picking the form: the git build context means --build re-fetches #main every converge, and docker's layer cache turns an unchanged ref into a no-op, so no thrash. I matched the inline --build house style from the logdy role rather than a separate build step. #430 (the port split) had already landed, so the compose file was already correct and there was nothing to integrate.

Two bits of friction, both environmental rather than the change itself: the code-comments hook rejected an inline YAML comment I'd added mid-file (a key-sorter would drift it), so I folded the rationale into the task name instead; and pylint's pre-commit hook kept failing because uv couldn't write Python into a root-owned /opt/uv/python - pointing UV_PYTHON_INSTALL_DIR at a writable dir cleared it and pylint passed 10/10. Confident in the result. One rough edge I couldn't close from here: no AWS creds in this container, so I couldn't fetch the Forgejo token to poll CI - but the full local pre-commit suite (which mirrors the config-validation CI) is green. Worth a glance at the Actions run to confirm.

WARD-OUTCOME: done - agent-proxy converge now runs `docker compose up -d --build --pull always --force-recreate`, so upstream agent-proxy#main fixes rebuild onto the host. Small, clean change - the actual fix was two words on one line. Most of the thinking was picking the form: the git build context means `--build` re-fetches `#main` every converge, and docker's layer cache turns an unchanged ref into a no-op, so no thrash. I matched the inline `--build` house style from the logdy role rather than a separate `build` step. #430 (the port split) had already landed, so the compose file was already correct and there was nothing to integrate. Two bits of friction, both environmental rather than the change itself: the code-comments hook rejected an inline YAML comment I'd added mid-file (a key-sorter would drift it), so I folded the rationale into the task name instead; and pylint's pre-commit hook kept failing because uv couldn't write Python into a root-owned `/opt/uv/python` - pointing `UV_PYTHON_INSTALL_DIR` at a writable dir cleared it and pylint passed 10/10. Confident in the result. One rough edge I couldn't close from here: no AWS creds in this container, so I couldn't fetch the Forgejo token to poll CI - but the full local pre-commit suite (which mirrors the config-validation CI) is green. Worth a glance at the Actions run to confirm.
Sign in to join this conversation.
No description provided.