How to fix health check command returned non-zero exit code: 1 in Docker

DockerINTERMEDIATEMEDIUMUpdated Dec 6, 2025ยท 224 views

This error occurs when a Docker container's HEALTHCHECK command fails, returning exit code 1 instead of 0. The fix involves debugging the health check command, adjusting timing parameters, and ensuring the target service is properly responding.

Quick fix

Fixes health check command returned non-zero exit code: 1

# View container health status
docker ps

# Get detailed health check information
docker inspect --format='{{json .State.Health}}' <container_name> | jq

# View the last 5 health check results
docker inspect <container_name> | jq '.State.Health.Log[-5:]'
  1. Inspect the health check status and logs
  2. Test the health check command manually
  3. Add or increase the start-period
  4. Fix the health check command syntax
docker โ€” error
# View container health status
docker ps
# Get detailed health check information
docker inspect --format='{{json .State.Health}}' <container_name> | jq
# View the last 5 health check results
docker inspect <container_name> | jq '.State.Health.Log[-5:]'
health check command returned non-zero exit code: 1

What this error means

The "health check command returned non-zero exit code: 1" error in Docker indicates that the HEALTHCHECK instruction defined in your Dockerfile or docker-compose.yml has failed. Docker's health check mechanism periodically runs a command inside your container to verify the service is working correctly. When the health check command exits with code 0, Docker marks the container as "healthy." When it exits with code 1 (or any non-zero code), Docker marks it as "unhealthy." This status is visible in `docker ps` output and can be used by orchestrators like Docker Swarm or Kubernetes to make decisions about container lifecycle. Common reasons for this error include: the health check endpoint not being ready yet during container startup, incorrect health check command syntax, the target service crashing or becoming unresponsive, or network/DNS issues preventing the health check from reaching the service.

Typical symptoms

  • Container shows '(unhealthy)' status in docker ps output
  • docker inspect shows Health.Status as 'unhealthy'
  • Container keeps restarting when used with depends_on condition: service_healthy
  • Docker Compose services fail to start with health check timeout errors
  • Orchestrators like Swarm or Kubernetes mark pods/services as unavailable
  • Application appears to work but container is marked unhealthy

Common causes

  1. Health check command failing because the service inside the container isn't ready yet (missing start-period)
  2. Incorrect health check command syntax or wrong endpoint URL
  3. Service inside container crashed or is not responding on the expected port
  4. Using exec form (brackets) when shell form is needed for pipes or environment variables
  5. Timeout value too short for the health check command to complete
  6. Missing '|| exit 1' causing non-standard exit codes from tools like curl
  7. Health check uses hostname/port only accessible from outside the container
  8. Required tools (curl, wget) not installed in the container image

How to fix "health check command returned non-zero exit code: 1"

1Inspect the health check status and logs

First, check the current health status and recent health check output:

bash
# View container health status
docker ps

# Get detailed health check information
docker inspect --format='{{json .State.Health}}' <container_name> | jq

# View the last 5 health check results
docker inspect <container_name> | jq '.State.Health.Log[-5:]'

The output shows the exit code and any output/error from each health check attempt. This is crucial for understanding why the check is failing.

Example output:

json
{
  "Status": "unhealthy",
  "FailingStreak": 3,
  "Log": [
    {
      "Start": "2024-01-15T10:00:00.000Z",
      "End": "2024-01-15T10:00:01.000Z",
      "ExitCode": 1,
      "Output": "curl: (7) Failed to connect to localhost port 8080"
    }
  ]
}
2Test the health check command manually

Run the health check command directly inside the container to debug it:

bash
# Get a shell in the running container
docker exec -it <container_name> sh

# Run your health check command manually
curl -f http://localhost:8080/health
echo $?  # Check the exit code

If you can't access a shell, run the command directly:

bash
docker exec <container_name> curl -f http://localhost:8080/health

Common issues to look for:
- Connection refused: Service not running or wrong port
- 404 Not Found: Endpoint doesn't exist
- Timeout: Service too slow or network issues
- Command not found: curl/wget not installed in image

3Add or increase the start-period

If the health check fails during container startup, add a start-period to give your application time to initialize:

In Dockerfile:

dockerfile
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

In docker-compose.yml:

yaml
services:
  web:
    image: myapp
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      start_period: 60s
      retries: 3

The start_period gives your application time to start up before health checks begin counting failures. During this period, failed health checks don't count toward the retry limit.

Recommended values:
- interval: 10-30 seconds
- timeout: 5-10 seconds
- start_period: Time your app needs to fully start (30s-5m depending on app)
- retries: 3-5 attempts

4Fix the health check command syntax

Ensure your health check command is correctly formatted:

Use shell form for commands with pipes or || exit 1:

dockerfile
# Correct - shell form
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1

# Correct - explicit shell form
HEALTHCHECK CMD ["sh", "-c", "curl -f http://localhost:8080/health || exit 1"]

The "|| exit 1" is important because curl and other tools may return exit codes other than 1 on failure. Docker only recognizes 0 (healthy) and 1 (unhealthy) - other exit codes are undefined behavior.

For docker-compose.yml:

yaml
healthcheck:
  # Shell form (string) - automatically uses shell
  test: curl -f http://localhost:8080/health || exit 1

  # Or CMD-SHELL explicitly
  test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]

  # Exec form - no shell, so || won't work
  test: ["CMD", "curl", "-f", "http://localhost:8080/health"]

Note: When using exec form (brackets without CMD-SHELL), there's no shell to handle ||, so the exit code passes through directly.

5Verify the service is accessible from inside the container

Health checks run inside the container, so they must use localhost or 127.0.0.1, not the external hostname or container name:

dockerfile
# Correct - use localhost inside the container
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1

# Wrong - container name only works from other containers
HEALTHCHECK CMD curl -f http://myservice:8080/health || exit 1

Verify the port is correct and the service is bound to all interfaces (0.0.0.0) or localhost:

bash
# Check what ports are listening inside the container
docker exec <container_name> netstat -tlnp
# or
docker exec <container_name> ss -tlnp

If your app binds to a specific interface, make sure the health check uses that address.

6Install required tools in your image

If curl or wget isn't available in your container, you have several options:

Option 1: Install curl in Dockerfile

dockerfile
# For Debian/Ubuntu
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

# For Alpine
RUN apk add --no-cache curl

Option 2: Use wget (often already installed)

dockerfile
HEALTHCHECK CMD wget --spider -q http://localhost:8080/health || exit 1

Option 3: Use native tools for minimal images

dockerfile
# For images with only basic tools, check if TCP port is open
HEALTHCHECK CMD nc -z localhost 8080 || exit 1

# Or use /dev/tcp in bash
HEALTHCHECK CMD bash -c 'echo > /dev/tcp/localhost/8080' || exit 1

Option 4: Add a custom health check script

dockerfile
COPY healthcheck.sh /healthcheck.sh
RUN chmod +x /healthcheck.sh
HEALTHCHECK CMD /healthcheck.sh
7Handle application-specific health endpoints

Configure your application to provide a proper health check endpoint:

Node.js/Express example:

javascript
app.get('/health', (req, res) => {
  // Check database connection, dependencies, etc.
  const isHealthy = checkDependencies();
  if (isHealthy) {
    res.status(200).send('OK');
  } else {
    res.status(503).send('Service Unavailable');
  }
});

Python/Flask example:

python
@app.route('/health')
def health():
    try:
        # Check dependencies
        db.session.execute('SELECT 1')
        return 'OK', 200
    except Exception:
        return 'Unhealthy', 503

For database containers:

yaml
# PostgreSQL
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U postgres"]
  interval: 10s
  timeout: 5s
  retries: 5

# MySQL
healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
  interval: 10s
  timeout: 5s
  retries: 5

# Redis
healthcheck:
  test: ["CMD", "redis-cli", "ping"]
  interval: 10s
  timeout: 5s
  retries: 5

Advanced notes

Understanding health check exit codes: Docker recognizes only two exit codes for health checks:
- 0: Success - container is healthy
- 1: Failure - container is unhealthy

Any other exit code (like curl's 22 for HTTP 4xx/5xx or 7 for connection refused) should be normalized to 1 using || exit 1.

Health check with pipefail: If your health check uses pipes, the exit code comes from the last command by default. Use set -o pipefail to fail if any command in the pipe fails:

dockerfile
HEALTHCHECK CMD ["sh", "-c", "set -o pipefail && curl -s http://localhost:8080/health | grep -q 'OK' || exit 1"]

Using depends_on with health checks: In Docker Compose, you can make services wait for dependencies to be healthy:

yaml
services:
  web:
    depends_on:
      db:
        condition: service_healthy
  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5

Auto-healing unhealthy containers: Docker doesn't automatically restart unhealthy containers. Options include:
1. Use Docker Swarm with replicas (it replaces unhealthy containers)
2. Use the willfarrell/autoheal container to monitor and restart unhealthy containers
3. Use Kubernetes liveness probes which do restart unhealthy pods

Debugging health checks in CI/CD: If health checks fail in CI but work locally:
- CI environments may have slower startup times - increase start_period
- Network restrictions may block health check requests
- Resource limits may cause timeouts - increase timeout value

Health check vs readiness: Health checks only determine if a container is working, not if it's ready to serve traffic. For Kubernetes, use both liveness (restart if unhealthy) and readiness (stop sending traffic if not ready) probes.

Frequently asked questions

What does "health check command returned non-zero exit code: 1" mean?
yml has failed. Docker's health check mechanism periodically runs a command inside your container to verify the service is working correctly. " This status is visible in docker ps output and can be used by orchestrators like Docker Swarm or Kubernetes to make decisions about container lifecycle.
How do I fix "health check command returned non-zero exit code: 1"?
This error occurs when a Docker container's HEALTHCHECK command fails, returning exit code 1 instead of 0. The fix involves debugging the health check command, adjusting timing parameters, and ensuring the target service is properly responding.
What causes "health check command returned non-zero exit code: 1"?
Health check command failing because the service inside the container isn't ready yet (missing start-period) Incorrect health check command syntax or wrong endpoint URL Service inside container crashed or is not responding on the expected port
Is the "health check command returned non-zero exit code: 1" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading