Dokploy / Dokploy/dokploy

Swarm Settings health check 'Test' field serializes as single string instead of JSON array, causing tasks to hang in 'Starting' forever

Open
#5,171 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
TypeScript
Stars
37.4k
Forks
3k
Avg merge
1d 3h
Merged PRs (30d)
73

Description

Description

The Health Check field under Advanced → Swarm Settings for an Application does not correctly serialize the Test array. Whatever string is pasted into the field ends up wrapped as a single-element array (a literal string), instead of being parsed as a proper Docker HEALTHCHECK Test array. This produces an invalid Swarm service healthcheck spec that Docker Engine rejects (Unknown healthcheck type '' (expected 'CMD')), which causes every task for the service to hang in Starting forever, which in turn blocks the service's internal DNS/VIP registration in the Swarm overlay network — the service becomes completely unroutable (Traefik gets NXDOMAIN resolving the service name) even though the underlying containers are running perfectly fine.

This is a nasty bug because:

  1. The app itself is 100% healthy (verified by curling the container's IP directly).
  2. docker service ls shows 0/N replicas indefinitely.
  3. There is no user-facing error in the Dokploy UI — the deploy appears to just hang.
  4. It also affects services that were never touched via the Advanced tab: a freshly-created Application via Dokploy's normal "create app" flow already ships with Healthcheck.Test: [""] (empty string) by default, which triggers the exact same Docker Engine warning and the exact same stuck-task/broken-DNS cascade — no manual healthcheck configuration is even required to hit this.
To Reproduce
  1. Create an Application in Dokploy, deploy it normally (no custom healthcheck set).

  2. Inspect the resulting Swarm service: docker service inspect <service> --format '{{json .Spec.TaskTemplate.ContainerSpec.Healthcheck}}' → shows {"Test":[""]}.

  3. docker service ps <service> will (inconsistently, but reproducibly under load/repeated redeploys) show tasks stuck in Starting forever, and docker service ls never reaches the desired replica count.

  4. Attempting to "fix" this by setting a Health Check in Advanced → Swarm Settings makes it worse in a different way. Example input pasted into the Test field:

    ["CMD","curl","-f","http://localhost:8080/"]
    

    (also tried the full object form {"Test":[...],"Interval":...} — same result for the Test portion)

    After clicking Deploy, the resulting live Swarm spec is:

    {"Test":["[\"CMD\",\"curl\",\"-f\",\"http://localhost:8080/\"]"],"Interval":10000000000,"Timeout":5000000000,"StartPeriod":5000000000,"Retries":3}
    

    Note Test has one element, and that element is the literal string I typed (brackets and all), not a 4-element array. Interval/Timeout/StartPeriod/Retries do get parsed correctly as numbers, so the numeric fields work — only Test is mishandled.

Current vs. Expected behavior

Current: Test field content is stored verbatim as a single array element, regardless of whether a JSON array or bare string is entered. Combined with the default Test: [""] on fresh services, this reliably produces Docker Engine's Unknown healthcheck type '' (expected 'CMD') warning and permanently stuck task convergence.

Expected: The Test field should either (a) accept and correctly parse a JSON array like ["CMD","curl","-f","http://localhost:8080/"] into a real multi-element Test array, or (b) if it's meant to be a single CMD-SHELL string, automatically wrap it as ["CMD-SHELL", "<input>"] rather than ["<input>"]. And by default (no health check configured), new services should get a semantically valid "no healthcheck" value (Test: ["NONE"]), not Test: [""].

Workaround

Bypass the Dokploy UI entirely and set the healthcheck directly via the Docker CLI against the already-created service:

docker service update --detach=false \
  --health-cmd "curl -f http://localhost:8080/ || exit 1" \
  --health-interval 10s \
  --health-timeout 5s \
  --health-retries 3 \
  --health-start-period 5s \
  <service-name>

This produces a correct {"Test":["CMD-SHELL","curl -f http://localhost:8080/ || exit 1"], ...} and the service converges immediately. Downside: the next time "Deploy" is clicked in the Dokploy UI for that app, it appears to re-apply the broken Test value from the stored (malformed) config, undoing the workaround.

Environment
  • Dokploy version: v0.30.0
  • Docker Engine version: 29.7.2
  • Single-node Swarm
  • Deployment type: Application (Dockerfile build → image pushed to GHCR → Dokploy pulls & deploys), start-first update order
Related issues

This seems adjacent to #4576 (endless restart loop with default healthcheck) and #3987 (Swarm rollback not reflected when tasks fail healthcheck) — all three point at the same general area (Docker Swarm healthcheck handling) being fragile, but this specific report is about the Test field losing its array structure when configured through the Advanced → Swarm Settings UI, plus the default Test: [""] on fresh services being invalid rather than a proper "no healthcheck" sentinel.

Impact

This caused a full production outage for us: a routine redeploy caused the frontend service to get permanently stuck at 0/N replicas with no visible error in the UI, and the service became completely unroutable (DNS NXDOMAIN for the service name inside the Swarm overlay network) even though the containers themselves were running and serving traffic correctly when hit directly by IP.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the Advanced → Swarm Settings Health Check Test field and the deploy path that builds the Docker Swarm healthcheck. Reproduce with the JSON array and with a new application without a healthcheck, then inspect docker service inspect output. Done means Test preserves array elements and an unset check produces a valid no-healthcheck value without tasks stuck in Starting.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, typescript
Domain
devops, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.