Health checks
Every image except cli ships a Docker HEALTHCHECK. It works with no configuration. Set HEALTHCHECK_PATH to check a route in your app instead of the web server alone.
What each variation checks
| Variation | Check | Passes when |
|---|---|---|
fpm-nginx | curl to http://localhost:${NGINX_HTTP_PORT}${HEALTHCHECK_PATH} | NGINX and PHP-FPM both answer |
fpm-apache | curl to http://localhost:${APACHE_HTTP_PORT}${HEALTHCHECK_PATH} | Apache and PHP-FPM both answer |
frankenphp | curl to http://localhost:${CADDY_HTTP_PORT}${HEALTHCHECK_PATH} | Caddy answers |
fpm | php-fpm-healthcheck | PHP-FPM's status page answers |
cli | None |
HEALTHCHECK_PATH defaults to /healthcheck. In fpm-nginx and fpm-apache, the web server passes /healthcheck to PHP-FPM's ping page, which returns OK. In frankenphp, Caddy returns OK itself. Neither one reaches your app.
The check follows the port variable when you change it. curl follows redirects and skips certificate checks, so the check still passes with SSL_MODE=full, where the HTTP port redirects to HTTPS. Requests to HEALTHCHECK_PATH stay out of the access log.
Check your app instead
Set HEALTHCHECK_PATH to a route in your app that returns 200, such as Laravel's /up:
services:
app:
image: my-org/my-app:1.0.0
environment:
HEALTHCHECK_PATH: "/up"
The check now fails when your app can't boot, not only when the web server is down. Pick a route that answers quickly.
In fpm-nginx and fpm-apache, S6 Overlay requests the same path at startup. It marks the web server ready once the path returns 200.
Laravel health checks covers /up and the checks for queue, Horizon, and scheduler containers.
Timing
All four checks use the same settings:
| Setting | Value |
|---|---|
start_period | 60s |
start_interval | 3s |
interval | 10s |
timeout | 3s |
retries | 3 |
During the first 60 seconds, Docker checks every 3 seconds (Docker Engine 25 and newer), and failures don't count. After that, three failures in a row mark the container unhealthy. Override any of these with healthcheck: in your Compose file.
What happens when a check fails
Docker and Docker Compose only mark a container unhealthy. They don't restart it. Compose uses health only for depends_on with condition: service_healthy.
Docker Swarm replaces unhealthy containers. During a rolling update, it waits for each new container to pass before it moves on.
Kubernetes ignores Docker's HEALTHCHECK. Point a readiness or liveness probe at the same path instead.
Containers that run another command
A container that runs php artisan queue:work or another command in place of the web server still inherits the image's health check. That check fails, because no web server is running. Give each of these containers its own healthcheck:. The Laravel health check scripts cover the common Laravel workers.