How to fix Container is starting (health: starting) in Docker

DockerBEGINNERLOWUpdated Dec 6, 2025· 348 views

This status indicates a Docker container is in its health check start period and hasn't yet passed a health check. The fix involves configuring an appropriate start_period, verifying the health check command works, and ensuring your application starts within the expected timeframe.

Quick fix

Fixes Container is starting (health: starting)

# View container health status
docker ps

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

# See the complete health check configuration
docker inspect <container_name> --format='{{json .Config.Healthcheck}}' | jq
  1. Check the current health check configuration and status
  2. Configure an appropriate start_period
  3. Use start_interval for faster initial checks (Docker 25.0+)
  4. Test the health check command manually
docker — error
# View container health status
docker ps
# Get detailed health configuration and status
docker inspect <container_name> --format='{{json .State.Health}}' | jq
# See the complete health check configuration
docker inspect <container_name> --format='{{json .Config.Healthcheck}}' | jq
Container is starting (health: starting)

What this error means

The "Container is starting (health: starting)" status in Docker indicates that a container with a configured HEALTHCHECK is still in its initialization phase. When Docker starts a container with a health check defined, the health status begins as "starting" and remains in this state until either a health check passes (changing to "healthy") or the start_period expires and enough consecutive checks fail (changing to "unhealthy"). This is normal behavior during container startup. The "starting" status exists specifically to give applications time to initialize before Docker begins counting health check failures. However, if a container stays stuck in "starting" status for an extended period, it typically indicates one of several problems: the health check command itself is failing, the application inside the container isn't starting properly, or the configured start_period and timing values need adjustment. The start_period parameter is particularly important for slow-starting applications like Java applications, databases, or services that need to load large datasets or establish connections before being ready to serve requests.

Typical symptoms

  • Container shows '(health: starting)' status in docker ps output
  • docker inspect shows Health.Status as 'starting' for an extended period
  • Services with depends_on condition: service_healthy are stuck waiting
  • Docker Compose deployment hangs waiting for dependent services
  • Container eventually transitions to 'unhealthy' after the start period expires
  • Application appears functional but container never becomes 'healthy'

Common causes

  1. Health check command is failing silently (wrong endpoint, missing tools)
  2. Application takes longer to start than the configured start_period allows
  3. Health check uses tools (curl, wget) not installed in the container image
  4. Insufficient start_period for slow-starting applications like databases or Java apps
  5. Health check endpoint not yet available when checks begin
  6. Using localhost which resolves to IPv6 (::1) while app binds only to IPv4
  7. start_interval parameter too long, delaying the first successful health check
  8. Network or DNS issues preventing health check from reaching the application

How to fix "Container is starting (health: starting)"

1Check the current health check configuration and status

First, inspect the container's health check settings and current state:

bash
# View container health status
docker ps

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

# See the complete health check configuration
docker inspect <container_name> --format='{{json .Config.Healthcheck}}' | jq

Example output showing a container stuck in "starting":

json
{
  "Status": "starting",
  "FailingStreak": 0,
  "Log": []
}

Check the Healthcheck config to see current timing values:

json
{
  "Test": ["CMD-SHELL", "curl -f http://localhost:8080/health"],
  "Interval": 30000000000,
  "Timeout": 10000000000,
  "StartPeriod": 0,
  "Retries": 3
}

Note: Times are in nanoseconds (30000000000 = 30 seconds).

2Configure an appropriate start_period

The start_period gives your application a grace period to initialize before health check failures count against it. Set this based on how long your application typically takes to start.

In Dockerfile:

dockerfile
# For a typical web application (30-60 second startup)
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

# For slow-starting apps like Java/databases (2-5 minute startup)
HEALTHCHECK --interval=30s --timeout=10s --start-period=300s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

In docker-compose.yml:

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

  database:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      start_period: 120s  # Databases need more time
      retries: 5

Recommended start_period values:
- Simple web apps: 30-60 seconds
- Node.js/Python apps: 30-90 seconds
- Java/Spring Boot apps: 120-300 seconds
- Databases (PostgreSQL, MySQL): 60-120 seconds
- Elasticsearch/Kafka: 180-300 seconds

3Use start_interval for faster initial checks (Docker 25.0+)

Docker Engine 25.0 introduced the start_interval parameter, which allows more frequent health checks during the start period for faster detection when a service becomes ready:

In Dockerfile:

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

In docker-compose.yml (Compose file version 3.9+):

yaml
services:
  web:
    image: myapp
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
      interval: 30s
      timeout: 10s
      start_period: 60s
      start_interval: 5s  # Check every 5s during start_period
      retries: 3

This configuration means:
- During the 60s start_period: health checks run every 5 seconds
- After start_period ends: health checks run every 30 seconds
- Container becomes "healthy" as soon as the first check passes

Note: start_interval requires start_period to be set. If you omit start_period, you'll get an error.

4Test the health check command manually

Verify that your health check command actually works inside the container:

bash
# Execute the health check command directly
docker exec <container_name> curl -f http://localhost:8080/health
echo "Exit code: $?"

# Or get a shell and test interactively
docker exec -it <container_name> sh

# Inside the container:
curl -f http://localhost:8080/health
echo $?

Common issues:

1. curl/wget not found: Use wget (often pre-installed) or add health check tools:

dockerfile
# Alpine - use wget instead of curl
HEALTHCHECK CMD wget --spider -q http://localhost:8080/health || exit 1

# Or install curl
RUN apk add --no-cache curl

2. Connection refused: Check the port and that your app binds to 0.0.0.0:

bash
docker exec <container_name> netstat -tlnp

3. IPv6 issues: Use 127.0.0.1 instead of localhost:

dockerfile
HEALTHCHECK CMD curl -f http://127.0.0.1:8080/health || exit 1
5Monitor health check progress

Watch the health check status in real-time to understand what's happening:

bash
# Watch container status continuously
watch -n 2 'docker ps --format "table {{.Names}}	{{.Status}}"'

# Monitor Docker events for health status changes
docker events --filter container=<container_name> --filter event=health_status

# Check health check logs as they accumulate
watch -n 5 'docker inspect <container_name> --format="{{json .State.Health}}" | jq'

Understanding the health check log:

During the start_period, health checks run but failures don't count against the retry limit. Once start_period ends, failed checks increment the FailingStreak counter:

json
{
  "Status": "starting",
  "FailingStreak": 0,
  "Log": [
    {
      "Start": "2024-01-15T10:00:00.000Z",
      "End": "2024-01-15T10:00:01.000Z",
      "ExitCode": 1,
      "Output": "curl: (7) Failed to connect"
    },
    {
      "Start": "2024-01-15T10:00:30.000Z",
      "End": "2024-01-15T10:00:30.500Z",
      "ExitCode": 0,
      "Output": "OK"
    }
  ]
}

When ExitCode becomes 0, the container transitions to "healthy".

6Handle depends_on with service_healthy condition

When using Docker Compose with health check dependencies, services won't start until dependencies are healthy:

yaml
services:
  web:
    image: myapp
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy

  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      start_period: 60s
      retries: 5

  redis:
    image: redis:7
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      start_period: 30s
      retries: 3

Known issue (Docker Compose): There's a known behavior where dependent services won't start until the full start_period has elapsed, even if the health check passes earlier. This was reported in docker/compose#11131.

Workaround: Use a shorter start_period with start_interval to balance fast healthy detection with tolerance for slow starts:

yaml
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U postgres"]
  interval: 30s
  timeout: 5s
  start_period: 30s
  start_interval: 2s  # Quick checks during startup
  retries: 5
7Optimize application startup time

If your application genuinely takes a long time to start, consider optimizing it:

1. Lazy initialization: Defer non-critical startup tasks

javascript
// Node.js - lazy database connection
let dbConnection;
app.get('/health', (req, res) => {
  res.status(200).send('OK');  // Basic health check
});

async function getDb() {
  if (!dbConnection) {
    dbConnection = await connectToDatabase();
  }
  return dbConnection;
}

2. Separate readiness from liveness:

yaml
# Basic health check that passes early
healthcheck:
  test: ["CMD-SHELL", "curl -f http://localhost:8080/health/live || exit 1"]
  interval: 10s
  start_period: 10s

3. Pre-warm in development/CI:

bash
# Wait script for CI pipelines
until docker inspect --format='{{.State.Health.Status}}' mycontainer | grep -q "healthy"; do
  echo "Waiting for container to be healthy..."
  sleep 2
done

4. Use multi-stage builds to reduce image size and startup time:

dockerfile
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]

Advanced notes

How start_period actually works: During the start_period, Docker still runs health checks at the specified interval, but failed checks don't count toward the retry limit. The first successful check immediately transitions the container to "healthy" status, regardless of how much time remains in the start_period.

Difference between start_period and start_interval:
- start_period: The grace period during which health check failures don't count against retries
- start_interval: (Docker 25.0+) The frequency of health checks during the start_period

Without start_interval, checks during the start_period run at the regular interval (e.g., every 30s). With start_interval set to 5s, checks run every 5 seconds during startup for faster healthy detection.

Health check timing math: Consider a container with this configuration:

yaml
interval: 30s
timeout: 10s
start_period: 60s
retries: 3

- First health check runs at T+30s (interval after start)
- During start_period (0-60s): failures don't count
- After start_period: 3 consecutive failures = unhealthy
- Worst case to unhealthy: 60s + (30s * 3) = 150s from container start

Compose version compatibility:
- start_period: Supported in Compose file version 2.3+
- start_interval: Supported in Compose file version 3.9+ with Docker Engine 25.0+

Debugging in orchestrators: In Docker Swarm or Kubernetes, a container stuck in "starting" can affect service availability:
- Swarm: Won't route traffic to container until healthy
- Kubernetes: Use livenessProbe.initialDelaySeconds equivalent to start_period

When containers never become healthy: If a container perpetually stays in "starting" then transitions to "unhealthy":
1. The health check command is fundamentally broken
2. The application never fully starts (crash loop, missing deps)
3. The start_period is too short for the application

Check docker logs <container_name> for application errors that might explain why the service isn't ready.

Frequently asked questions

What does "Container is starting (health: starting)" mean?
The "Container is starting (health: starting)" status in Docker indicates that a container with a configured HEALTHCHECK is still in its initialization phase. When Docker starts a container with a health check defined, the health status begins as "starting" and remains in this state until either a health check passes (changing to "healthy") or the start_period expires and enough consecutive checks fail (changing to "unhealthy"). This is normal behavior during container startup.
How do I fix "Container is starting (health: starting)"?
This status indicates a Docker container is in its health check start period and hasn't yet passed a health check. The fix involves configuring an appropriate start_period, verifying the health check command works, and ensuring your application starts within the expected timeframe.
What causes "Container is starting (health: starting)"?
Health check command is failing silently (wrong endpoint, missing tools) Application takes longer to start than the configured start_period allows Health check uses tools (curl, wget) not installed in the container image
Is the "Container is starting (health: starting)" error serious?
It is a low-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading