Health checks
A container can look healthy while your app is broken. A bad .env value or a crashing service provider still leaves the web server answering. Add one line, and Docker checks that Laravel itself boots.
services:
app:
image: my-org/my-app:1.0.0
ports:
- "80:8080"
environment:
HEALTHCHECK_PATH: "/up"
Laravel 11 and newer ship the /up health route. It returns 200 when your app boots and 500 when it throws an exception. With the official PHP images, you'd write this health check yourself.
/up checks what your users hit
| What gets checked | Default /healthcheck | HEALTHCHECK_PATH: "/up" |
|---|---|---|
| The web server responds | ✅ | ✅ |
| Laravel boots | ❌ | ✅ |
The default check never reaches your app. That's fine for a plain PHP site. For Laravel, /up catches the failures that matter.
When /up fails, Docker marks the container unhealthy. Docker Swarm replaces it. During a rolling update, Swarm waits for each new container to pass. If one fails, the update pauses instead of rolling a broken release onto every replica.
HEALTHCHECK_PATH works in fpm-nginx, fpm-apache, and frankenphp. Health checks explains the timing and what each variation checks.
Add your own checks to /up
Laravel fires a DiagnosingHealth event on each request to /up. Listen for it to check your database, cache, or anything else your app needs. Throw an exception, and /up returns 500.
Keep the checks fast. Docker runs the health check every 10 seconds and gives it 3 seconds to answer. Check only what the container can't run without: when a database check fails, Swarm replaces every app container at once.
Every worker gets a check
Queue, Horizon, and scheduler containers don't run a web server. The image's built-in check fails in them. Every image ships a script for each Laravel worker instead:
| Script | What it runs | Use it for |
|---|---|---|
healthcheck-horizon | php artisan horizon:status | Horizon |
healthcheck-nightwatch | php artisan nightwatch:status | Nightwatch |
healthcheck-octane | php artisan octane:status | Octane |
healthcheck-queue | pgrep -f "queue:work" | Queue |
healthcheck-reverb | pgrep -f "reverb:start" | Reverb |
healthcheck-schedule | pgrep -f "schedule:work" | Task scheduler |
Set the script as the container's healthcheck:
services:
queue:
image: my-org/my-app:1.0.0
command: ["php", "artisan", "queue:work"]
stop_signal: SIGTERM
healthcheck:
test: ["CMD", "healthcheck-queue"]
start_period: 10s
The pgrep scripts confirm the process is running. They don't check that it's processing jobs. The Artisan scripts ask Laravel for the worker's status, and they boot your app on every check.
Running workers has a full Compose file with the web server, Horizon, and the scheduler, each with its health check.