Docker health checks provide a standardized way to monitor the health status of your containers. When you integrate health checks into your Docker workflow, you gain automatic detection of failing services, better orchestration decisions from Docker Compose and Swarm, and improved debugging capabilities. This guide walks you through implementing Docker health checks effectively, with practical examples you can apply to your projects.
Understanding Docker Health Checks
A health check is a command that Docker runs inside your container to determine whether the container is working correctly. Unlike basic container status, health checks evaluate the actual application state. Docker periodically executes the health check command and tracks the result: starting (initial state), healthy, unhealthy, or starting again after a failure.
Health checks become essential when your application has internal dependencies or complex initialization sequences. For instance, a web application might start its process before database connections are ready. Without health checks, Docker considers the container running immediately after the process starts, even if the application isn’t actually functional.
Implementing Health Checks in Your Dockerfile
The HEALTHCHECK instruction defines a command Docker uses to test container health. Here’s a practical example for a Node.js application:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD node -e "
const http = require('http');
const options = { hostname: 'localhost', port: 3000, path: '/health', method: 'GET' };
const req = http.request(options, (res) => {
process.exit(res.statusCode === 200 ? 0 : 1);
});
req.on('error', () => process.exit(1));
req.end();
"
CMD ["node", "server.js"]
The --interval flag specifies how often Docker runs the health check (30 seconds in this case). The --timeout flag limits how long each check can run before being considered failed. The --start-period flag gives your application time to initialize before the first health check runs, preventing false negatives during startup. The --retries flag determines how many consecutive failures trigger an unhealthy status.
For a Python Flask application, the health check might look different:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "
import urllib.request
import sys
try:
response = urllib.request.urlopen('http://localhost:5000/api/health', timeout=5)
sys.exit(0) if response.getcode() == 200 else sys.exit(1)
except Exception:
sys.exit(1)
"
CMD ["python", "app.py"]
HEALTHCHECK Parameter Reference
Each parameter controls a different aspect of the check behavior:
- –interval: How often to run the healthcheck (default: 30s)
- –timeout: How long to wait for the check to complete (default: 30s)
- –start-period: Grace period after container starts before first check (default: 0s). Set this to at least your application’s startup time to avoid false failures.
- –retries: Number of consecutive failures before marking unhealthy (default: 3)
When a healthcheck fails repeatedly, Docker automatically restarts the container in orchestration platforms like Docker Swarm, providing self-healing capabilities for your infrastructure.
Health Checks with Docker Compose
When running multi-container applications with Docker Compose, health checks enable service dependencies to wait for dependencies to become healthy. This prevents race conditions where one service starts before its dependencies are ready.
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d app"]
interval: 10s
timeout: 5s
retries: 5
api:
build: .
ports:
- "3000:3000"
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: postgresql://user:password@postgres:5432/app
nginx:
image: nginx:alpine
ports:
- "80:80"
depends_on:
api:
condition: service_started
The key here is the condition: service_healthy directive. Docker Compose waits for the postgres container to report healthy status before starting the api service. This eliminates the need for custom wait scripts in your application startup logic.
Monitoring Container Health
Once health checks are in place, you can inspect container status using the Docker CLI:
docker ps
The output includes a status column showing health check results. For detailed information:
docker inspect --format='{{.State.Health}}' container_name
This returns the full health status object including status, failing streak, and check logs. In production environments, you might want to aggregate this data using monitoring tools. The supermemory skill can help you track health check patterns over time, creating a knowledge base of common failure modes and their resolutions.
For log aggregation, you can filter health check results:
docker inspect --format='{{range .State.Health.Log}}{{.Output}}{{"\n"}}{{end}}' container_name
This extracts just the output from health check commands, useful for debugging why checks are failing.
Health Checks for Different Application Types
Different application types require different health check strategies. A REST API typically checks an endpoint that validates database connectivity and critical dependencies. A background worker might check that it can connect to message queues and that processing queues are not growing unbounded.
For Redis:
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD redis-cli ping
For MongoDB:
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD mongosh --quiet --eval "db.adminCommand('ping')"
For custom scripts included in your image, ensure they’re executable:
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
CMD /app/scripts/healthcheck.sh
Best Practices
Keep health checks lightweight. They’re running continuously, so avoid expensive operations. A simple connectivity test to critical dependencies usually suffices.
Place health checks in your application rather than relying on process existence. A process can be running while deadlocked or misbehaving. Application-level health checks catch these scenarios.
Set appropriate start periods for slow-starting applications. Too short, and you’ll get false failures during normal startup. Too long, and you delay detection of actual startup failures.
Use descriptive exit codes. Exit 0 means healthy, anything else indicates problems. If your application has multiple failure modes, consider using different exit codes and monitoring the health check output.
Document your health check endpoints. If your API exposes a /health endpoint, document what it validates. Teams using the tdd skill often include health check validation in their test suites, ensuring the health endpoint accurately reflects system state.
Automation with Claude Code
When building Docker-based applications with Claude Code, you can automate health check generation. The supermemory skill stores your project patterns, so health checks for similar services follow consistent patterns. The pdf skill helps generate documentation for your health check configurations. The frontend-design skill ensures any health dashboard you build follows consistent UI patterns.
Related Reading
- Claude Code for Beginners: Complete Getting Started Guide
- Best Claude Skills for Developers in 2026
- Claude Skills Guides Hub
Built by theluckystrike — More at zovo.one