Stage 4 · Provision
Production Containers
Health Checks
Define Docker HEALTHCHECK instructions, readiness endpoints, startup probes, and failure thresholds.
Why Health Checks?
A running container is not necessarily a healthy container. The process might be running but unable to serve requests — waiting for database connections, running out of memory, or stuck in a deadlock. Health checks let Docker and orchestrators detect and restart unhealthy containers.
HEALTHCHECK Instruction
# Basic health check
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# For applications without curl
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"
# Disable health checks (not recommended)
HEALTHCHECK NONEThe HEALTHCHECK instruction tells Docker how to test if the container is healthy. Docker runs the command periodically and marks the container as healthy or unhealthy based on the exit code.
Readiness Endpoints
A readiness endpoint checks if the application can serve requests. It should verify database connections, cache availability, and downstream dependencies. Return HTTP 200 if ready, any other status code if not.
app.get('/health', async (req, res) => {
try {
// Check database connection
await db.query('SELECT 1');
// Check Redis connection
await redis.ping();
// Check disk space
const disk = await checkDiskSpace('/');
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
checks: {
database: 'ok',
redis: 'ok',
disk: disk.available > 1000000000 ? 'ok' : 'low'
}
});
} catch (error) {
res.status(503).json({
status: 'unhealthy',
error: error.message
});
}
});A good health check verifies all critical dependencies, not just that the HTTP server is listening. The /health endpoint should be fast (under 1 second) and lightweight.
Health Check States
| State | Meaning | Action |
|---|---|---|
| starting | Health check not yet run | Wait |
| healthy | Last check passed | None |
| unhealthy | Check failed retries times | Restart container |
# View health status in docker ps
docker ps
# CONTAINER ID IMAGE STATUS
# abc123 myapp Up 5 minutes (healthy)
# Inspect health check details
docker inspect --format '{{json .State.Health}}' mycontainer | jq
# View health check logs
docker inspect --format '{{range .State.Health.Log}}{{.Output}}{{end}}' mycontainerdocker ps shows health status in the STATUS column. docker inspect provides detailed health check history including each check's output and exit code.
Compose Healthchecks
services:
api:
image: myapp
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3Compose healthchecks are used with depends_on condition: service_healthy to ensure services start in the correct order. start_period gives the application time to initialize before checks begin failing.
Best Practices
- Always define HEALTHCHECK in Dockerfiles for production images
- Make health checks fast — under 1 second
- Verify critical dependencies, not just HTTP availability
- Use start_period to allow for application initialization
- Set appropriate retry counts to avoid false positives
- Monitor health check metrics in production
- Use health checks with depends_on in Compose
For Alpine-based images without curl, use wget: wget -qO- http://localhost:3000/health || exit 1. For distroless images, copy a static health check binary into the image.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.