Ghost docker healthcheck that works
If you have ever run Ghost in a Docker container, you know that a “running” container doesn’t always mean a “working” site. Whether it’s a database connection that dropped or a service that hung during a configuration change, Docker’s default behavior—simply checking if the process is alive—is often insufficient for real-world reliability.
I’ve experimented with several ways to verify that Ghost is actually capable of serving content, and I've landed on a wget approach that handles the common hurdles: SSL redirects, proxy headers, and specific domain routing.
The Challenge
Ghost is picky about how it’s accessed. If you have your url set to an https address in your configuration, Ghost will often try to redirect any plain http request (like a standard healthcheck) back to the secure version. If your container is behind a reverse proxy, this can lead to redirect loops or “unhealthy” statuses even when the site is fine.
The Solution
To get a “true” health status, we need to mimic a legitimate request coming from your proxy. Here is the configuration I use in my docker-compose.yml:
YAML
healthcheck:
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider --header='Host: www.dima.pm' --header='X-Forwarded-Proto: https' http://localhost:2368/ghost/api/admin/site/ || exit 1"]
interval: 60s
timeout: 10s
retries: 3
start_period: 30s
Why this works:
--spider&--quiet: This tellswgetto act like a web crawler. It checks if the page exists but doesn't actually download any files, keeping the check extremely lightweight and silent in your logs.- The
HostHeader: Ghost frequently ignores requests that don't match its configured domain. By passing--header='Host: www.dima.pm', we ensure Ghost recognizes the request as valid for the site it's hosting. X-Forwarded-Proto: https: This is the secret sauce. It tells the Ghost internal service, “Hey, this request already passed through a secure proxy.” This prevents Ghost from triggering a 301 redirect to HTTPS, which would otherwise cause the healthcheck to fail.- The Endpoint: Instead of hitting the homepage (which might be heavy), we hit
/ghost/api/admin/site/. This is a lightweight API endpoint that proves the core application and its underlying database connection are fully operational. start_period: 30s: Ghost and its database (especially if you're using MySQL) need a moment to breathe before they start accepting connections. This 30-second window prevents Docker from killing the container while it's still performing its initial boot.
Benefits
By using this specific check, you reduce maintenance overhead by ensuring that if Ghost enters a “zombie” state—where the process is running but the site is down—Docker will automatically catch it, mark it as unhealthy, and trigger a restart.
This is a critical layer of stability for any self-hosted blog, ensuring your content stays available even when you aren't actively monitoring the server.