- Fixed 4 broken markdown links (bad relative paths in See Also sections) - Corrected n8n port binding to 127.0.0.1:5678 (matches actual deployment) - Updated SnapRAID article with actual majorhome paths (/majorRAID, disk1-3) - Converted 67 Obsidian wikilinks to relative markdown links or plain text - Added YAML frontmatter to 35 articles missing it entirely - Completed frontmatter on 8 articles with missing fields Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4.0 KiB
title, domain, category, tags, status, created, updated
| title | domain | category | tags | status | created | updated | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Docker Healthchecks | selfhosting | docker |
|
published | 2026-03-23 | 2026-03-23 |
Docker Healthchecks
A Docker healthcheck tells the daemon (and any monitoring tool) whether a container is actually working — not just running. Without one, a container shows as Up even if the app inside is crashed, deadlocked, or waiting on a dependency.
Why It Matters
Tools like Uptime Kuma report containers without healthchecks as:
Container has not reported health and is currently running. As it is running, it is considered UP. Consider adding a health check for better service visibility.
A healthcheck upgrades that to a real (healthy) or (unhealthy) status, making monitoring meaningful.
Basic Syntax (docker-compose)
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
| Field | Description |
|---|---|
test |
Command to run. Exit 0 = healthy, non-zero = unhealthy. |
interval |
How often to run the check. |
timeout |
How long to wait before marking as failed. |
retries |
Failures before marking unhealthy. |
start_period |
Grace period on startup before failures count. |
Common Patterns
HTTP service (wget — available in Alpine)
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:2368/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
HTTP service (curl)
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
MySQL / MariaDB
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-psecret"]
interval: 10s
timeout: 5s
retries: 3
start_period: 20s
PostgreSQL
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
Redis
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
TCP port check (no curl/wget available)
healthcheck:
test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
interval: 30s
timeout: 5s
retries: 3
Using Healthchecks with depends_on
Healthchecks enable proper startup ordering. Instead of a fixed sleep, a dependent container waits until its dependency is actually ready:
services:
app:
depends_on:
db:
condition: service_healthy
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 3
start_period: 20s
This prevents the classic race condition where the app starts before the database is ready to accept connections.
Checking Health Status
# See health status in container list
docker ps
# Get detailed health info including last check output
docker inspect --format='{{json .State.Health}}' <container> | jq
Ghost Example
Ghost (Alpine-based) uses wget rather than curl:
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:2368/ghost/api/v4/admin/site/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
Gotchas & Notes
- Alpine images don't have
curlby default — usewgetor install curl in the image. start_periodis critical for slow-starting apps (databases, JVM services). Failures during this window don't count towardretries.CMDvsCMD-SHELL— useCMDfor direct exec (no shell needed),CMD-SHELLwhen you need pipes,&&, or shell builtins.- Uptime Kuma will pick up Docker healthcheck status automatically when monitoring via the Docker socket — no extra config needed.