Health Checks
A health check endpoint lets your load balancer, container orchestrator, or uptime monitor confirm that your application is alive and its dependencies are reachable. Without one, a deployment that starts the web server but fails to connect to the database will silently serve errors until a user reports them.
A Simple Health Route
The minimum viable health check is a route that returns HTTP 200 when the app is running:
Route::get('/health', fn () => response()->json(['status' => 'ok']));
This is enough for a basic uptime monitor. For anything beyond that, you want to verify your critical dependencies too.
Checking Dependencies
A more useful health check tests whether the application can actually do its job:
Route::get('/health', function () {
$checks = [];
// Database
try {
DB::connection()->getPdo();
$checks['database'] = 'ok';
} catch (\Exception) {
$checks['database'] = 'fail';
}
// Cache
try {
Cache::put('health', true, 5);
$checks['cache'] = Cache::get('health') === true ? 'ok' : 'fail';
} catch (\Exception) {
$checks['cache'] = 'fail';
}
// Queue (check that the failed_jobs table is reachable)
try {
DB::table('failed_jobs')->count();
$checks['queue'] = 'ok';
} catch (\Exception) {
$checks['queue'] = 'fail';
}
$healthy = !in_array('fail', $checks);
return response()->json([
'status' => $healthy ? 'ok' : 'degraded',
'checks' => $checks,
], $healthy ? 200 : 503);
});
Return a 503 status code when any critical dependency is down. Load balancers use this to stop routing traffic to the unhealthy instance.
Using spatie/laravel-health
For production applications, the spatie/laravel-health package provides a structured approach with built-in checks for databases, Redis, queues, disk space, and more:
composer require spatie/laravel-health
php artisan health:publish
Register your checks in a service provider:
use Spatie\Health\Facades\Health;
use Spatie\Health\Checks\Checks\DatabaseCheck;
use Spatie\Health\Checks\Checks\RedisCheck;
use Spatie\Health\Checks\Checks\QueueCheck;
use Spatie\Health\Checks\Checks\UsedDiskSpaceCheck;
public function boot(): void
{
Health::checks([
DatabaseCheck::new(),
RedisCheck::new(),
QueueCheck::new(),
UsedDiskSpaceCheck::new()->warnWhenUsedSpaceIsAbovePercentage(70),
]);
}
The package exposes a /health route automatically and can store results so you do not hammer your dependencies on every request.
Laravel 11+ Built-in Health Route
Laravel 11 ships with a built-in /up route that returns a 200 response when the application starts. You can find it in bootstrap/app.php. This is suitable for simple liveness probes but does not check dependencies.
Protecting the Endpoint
Health check endpoints should not be publicly accessible in detail. Consider two approaches:
Return minimal information publicly, detail only internally:
Route::get('/health', function () {
// Full detail only for requests from internal networks
if (!request()->is('192.168.*')) {
return response()->json(['status' => 'ok']);
}
// ... detailed checks
});
Use middleware to restrict access:
Route::get('/health', HealthController::class)
->middleware('throttle:health');
Tips
- Keep the health endpoint fast. Checks that take longer than a second will cause load balancer timeouts.
- Do not run migrations or heavy operations in a health check.
- Use a separate route file or exclude the health route from CSRF protection and authentication middleware.
- Pair the health endpoint with an external uptime monitor (UptimeRobot, Better Uptime) that polls it every minute.