Skip to content

fix(compose): stop double-wrapping healthcheck test commands - #264

Merged
vsilent merged 1 commit into
devfrom
fix/healthcheck-test-form
Sep 21, 2026
Merged

vsilent merged 1 commit into
devfrom
fix/healthcheck-test-form

Conversation

@vsilent

@vsilent vsilent commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Every healthcheck written the way the reference documents came out broken:

/bin/sh: 1: CMD-SHELL: not found

Docker wraps a plain string test in CMD-SHELL itself, so a string that already spells the prefix out gets wrapped twice and the container tries to execute a program named CMD-SHELL. The prefix only means anything in the list form. The generator was emitting the author's string verbatim.

Observed on a real deploy: both stackpilot services that declare a healthcheck came up unhealthy while the application itself was fine. Harmless on its own, but the state is frozen into a baked snapshot, and a stack using depends_on: condition: service_healthy would never start.

The decision now lives in one place. compose_service_sync already had healthcheck_test_value doing this correctly for the server-side path; the generator delegates to it and only renders the result as inline YAML. That also picks up a subtlety a second implementation would have missed: CMD executes argv directly, so a command containing &&, |, $ or redirection is emitted as CMD-SHELL instead. An explicit list written by the author now passes through untouched.

The reference said test: "CMD pg_isready -U postgres" and left it there. It now documents all three accepted forms, what each one runs, and why the prefix works here but not in a plain compose file.

Every healthcheck written the way the reference documents came out broken:

  /bin/sh: 1: CMD-SHELL: not found

Docker wraps a plain string `test` in `CMD-SHELL` itself, so a string
that already spells the prefix out gets wrapped twice and the container
tries to execute a program named `CMD-SHELL`. The prefix only means
anything in the list form. The generator was emitting the author's
string verbatim.

Observed on a real deploy: both stackpilot services that declare a
healthcheck came up unhealthy while the application itself was fine.
Harmless on its own, but the state is frozen into a baked snapshot, and
a stack using `depends_on: condition: service_healthy` would never
start.

The decision now lives in one place. `compose_service_sync` already had
`healthcheck_test_value` doing this correctly for the server-side path;
the generator delegates to it and only renders the result as inline
YAML. That also picks up a subtlety a second implementation would have
missed: `CMD` executes argv directly, so a command containing `&&`, `|`,
`$` or redirection is emitted as `CMD-SHELL` instead. An explicit list
written by the author now passes through untouched.

The reference said `test: "CMD pg_isready -U postgres"` and left it
there. It now documents all three accepted forms, what each one runs,
and why the prefix works here but not in a plain compose file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vsilent
vsilent merged commit 2623fed into dev Sep 21, 2026
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants