← All posts Docker

Docker container is unhealthy but still running: what next?

The Tryssh team ·

When interpreting health-check failures separately from process state, speed comes from ordering the investigation—not from typing more commands. Start at the user-visible edge, preserve the first failure, and work inward until one explanation accounts for the evidence.

A container is a process with isolation and lifecycle rules, not a tiny permanent server. Good diagnosis separates image, runtime, network, storage, and host state.

What this question is really asking

The search intent behind this problem is interpreting health-check failures separately from process state. That wording matters because it sets the boundary of the investigation. A vague request to “fix production” invites unrelated changes; a precise question produces a testable hypothesis and a clear finish line.

Write the expected healthy behavior in one sentence, then capture the exact error and first occurrence. Note whether the symptom is global or limited by tenant, route, node, or region. That boundary prevents a local failure from triggering a fleet-wide repair.

Start with evidence, not remediation

Run a compact read-only sequence:

docker inspect app --format '{{json .State.Health}}'
docker inspect app --format '{{json .Config.Healthcheck}}'
docker logs --since 15m app
docker stats --no-stream app

Each command should answer one question. Do not collect output simply because it looks technical. Mark what confirms normal behavior, what contradicts the working theory, and what is still unknown. If a command is unavailable, record that fact rather than silently replacing it with a riskier action.

The central interpretation for this case is: Health checks execute inside a specific environment with their own timeout and dependencies. Reproduce the exact check before changing the application.

Build the failure chain

Describe the system as a path from the user to the dependency that completes the request. Then place each observation on that path. A strong explanation accounts for the symptom, the timing, and why healthy-looking components did not prevent the failure.

Use this sequence:

  1. Reproduce the exact external symptom.
  2. Bound which hosts, tenants, regions, or requests are affected.
  3. Compare desired configuration with effective runtime state.
  4. Read the smallest log window around the first failure.
  5. Check saturation, errors, traffic, and recent changes.
  6. State one hypothesis and the observation that could disprove it.
  7. Choose the smallest reversible test.

If the evidence does not converge, widen one boundary at a time. Jumping from application logs to a fleet-wide restart skips the layers most likely to explain the problem.

Four traps during diagnosis

Following the loudest log line. Old warnings and downstream errors can be more visible than the initiating failure. Correlate by time and request path.

Testing from the wrong location. A localhost check skips DNS, firewalls, proxies, TLS, and routing that users depend on.

Confusing absence of evidence with health. Missing metrics may mean the collector is broken. Silence is a finding, not proof.

Skipping the negative test. State what observation would disprove the current theory before changing anything.

Make the repair safely

Write the proposed command, expected effect, verification, and rollback before executing it. Validate syntax before reload. Keep a recovery session open during access or firewall work. For data changes, confirm backup age and restoration procedure—not merely that a backup job reported success.

After the change, test the original symptom from outside the host. Then check the adjacent failure modes: latency, errors, resource pressure, retry volume, and data correctness. “The process started” is not the same as “the service recovered.”

Turn this article into a runbook

Capture the symptom, three highest-signal checks, decision tree, unsafe shortcuts, and recovery command. Include a date and owner so stale behavior is reviewed instead of copied forever.

For the underlying model, consult Docker Engine documentation. Product behavior and defaults change, so first-party documentation should win over copied snippets.

Related Tryssh guides

Continue with docker container keeps exiting, docker disk space, and docker compose production. These connect the immediate symptom to access safety, incident response, and durable operating practice.

Investigate it with Tryssh

Tryssh is a native macOS SSH workspace built for this evidence-first loop. Its copilot can run safe read-only checks, retain host-specific context, and explain combined output. The visible terminal remains separate, SSH secrets stay in the macOS Keychain, and commands that change state wait for explicit approval.

That boundary is especially useful under pressure: automation gathers facts quickly, while the operator remains responsible for blast radius. Download Tryssh for macOS and rehearse the runbook on a non-production host before the next incident.