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:
- The app itself is 100% healthy (verified by curling the container's IP directly).
docker service ls shows 0/N replicas indefinitely.
- There is no user-facing error in the Dokploy UI — the deploy appears to just hang.
- 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
-
Create an Application in Dokploy, deploy it normally (no custom healthcheck set).
-
Inspect the resulting Swarm service: docker service inspect <service> --format '{{json .Spec.TaskTemplate.ContainerSpec.Healthcheck}}' → shows {"Test":[""]}.
-
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.
-
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.
Description
The Health Check field under Advanced → Swarm Settings for an Application does not correctly serialize the
Testarray. 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 DockerHEALTHCHECKTestarray. 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 inStartingforever, which in turn blocks the service's internal DNS/VIP registration in the Swarm overlay network — the service becomes completely unroutable (Traefik getsNXDOMAINresolving the service name) even though the underlying containers are running perfectly fine.This is a nasty bug because:
docker service lsshows0/Nreplicas indefinitely.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
Create an Application in Dokploy, deploy it normally (no custom healthcheck set).
Inspect the resulting Swarm service:
docker service inspect <service> --format '{{json .Spec.TaskTemplate.ContainerSpec.Healthcheck}}'→ shows{"Test":[""]}.docker service ps <service>will (inconsistently, but reproducibly under load/repeated redeploys) show tasks stuck inStartingforever, anddocker service lsnever reaches the desired replica count.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:
(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
Testhas one element, and that element is the literal string I typed (brackets and all), not a 4-element array.Interval/Timeout/StartPeriod/Retriesdo get parsed correctly as numbers, so the numeric fields work — onlyTestis mishandled.Current vs. Expected behavior
Current:
Testfield content is stored verbatim as a single array element, regardless of whether a JSON array or bare string is entered. Combined with the defaultTest: [""]on fresh services, this reliably produces Docker Engine'sUnknown healthcheck type '' (expected 'CMD')warning and permanently stuck task convergence.Expected: The
Testfield should either (a) accept and correctly parse a JSON array like["CMD","curl","-f","http://localhost:8080/"]into a real multi-elementTestarray, 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"]), notTest: [""].Workaround
Bypass the Dokploy UI entirely and set the healthcheck directly via the Docker CLI against the already-created service:
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 brokenTestvalue from the stored (malformed) config, undoing the workaround.Environment
v0.30.029.7.2start-firstupdate orderRelated 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/Nreplicas with no visible error in the UI, and the service became completely unroutable (DNSNXDOMAINfor the service name inside the Swarm overlay network) even though the containers themselves were running and serving traffic correctly when hit directly by IP.