← Laravel

Health checks in Laravel

How to add a health check endpoint to a Laravel app: what to check, a controller example, spatie/laravel-health, the built-in /up route and access.

On this page
  1. The built-in /up route
  2. What to check
  3. A health check controller
  4. Using spatie/laravel-health
  5. Protecting the endpoint
  6. Practical rules
  7. Health checks and uptime monitoring

A health check is an endpoint that load balancers, container orchestrators and uptime monitors poll to ask one question: can this instance serve traffic? It returns HTTP 200 when everything it depends on works and HTTP 503 when something does not. Without one, a deployment that starts the web server but cannot reach the database will serve errors until a user reports them.

Load balancers use the status code to stop sending traffic to a failing instance. Orchestrators such as Kubernetes use it to decide whether to restart a container. Monitoring tools use it to raise an alert.

The built-in /up route

Laravel 11 and later ship with an /up route, configured in bootstrap/app.php. It returns 200 once the application boots. That is enough for a liveness probe, but it does not check any dependencies.

->withRouting(
    web: __DIR__.'/../routes/web.php',
    health: '/up',
)

What to check

A useful health check tests each thing the application needs to do its job. Keep every check cheap, because the endpoint runs every few seconds or every minute.

  • Database: Run SELECT 1 or open the connection. This catches a crashed database, an exhausted connection pool, or a network problem between the app and the database server.
  • Cache: Write a test key and read it back. A failing cache may not take the app down, but it slows it and can cause subtle bugs.
  • Queue: Check the queue backend is reachable, or that jobs are being processed.
  • Disk space: A full disk causes failures that are hard to diagnose if the app writes logs, uploads or temporary files locally.
  • External services: Payment gateways, email providers and data sources. Give each one a short timeout and treat a timeout as unhealthy, so a slow third party cannot make your own health check time out.
  • Application checks: A scheduled task that ran recently, or a configuration value that must be present. Add these as you find the failures that matter to your app.

A health check controller

This controller runs each check, records the result, and returns a JSON body that names the failing component.

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class HealthController
{
    public function __invoke(): JsonResponse
    {
        $checks = [
            'database' => $this->check(fn () => DB::select('SELECT 1')),
            'cache' => $this->check(function () {
                Cache::put('_health_check', true, 10);

                return Cache::get('_health_check') === true;
            }),
            'queue' => $this->check(fn () => DB::table('failed_jobs')->count()),
        ];

        $healthy = ! in_array('fail', $checks, true);

        return new JsonResponse([
            'status' => $healthy ? 'healthy' : 'unhealthy',
            'checks' => $checks,
            'timestamp' => now()->toIso8601String(),
        ], $healthy ? 200 : 503);
    }

    private function check(callable $callback): string
    {
        try {
            return $callback() === false ? 'fail' : 'ok';
        } catch (\Throwable) {
            return 'fail';
        }
    }
}

The queue check here only confirms the failed_jobs table is reachable. Swap it for a check against your real queue backend if you use Redis or SQS.

Register the route in routes/web.php:

use App\Http\Controllers\HealthController;

Route::get('/health', HealthController::class);

An unhealthy response tells you which part failed:

{
  "status": "unhealthy",
  "checks": {
    "database": "ok",
    "cache": "fail",
    "queue": "ok"
  },
  "timestamp": "2026-02-04T12:00:00+00:00"
}

Using spatie/laravel-health

For production apps, the spatie/laravel-health package gives you ready-made checks for databases, Redis, queues, disk space and more.

composer require spatie/laravel-health
php artisan health:publish

Register the checks in a service provider:

use Spatie\Health\Checks\Checks\DatabaseCheck;
use Spatie\Health\Checks\Checks\QueueCheck;
use Spatie\Health\Checks\Checks\RedisCheck;
use Spatie\Health\Checks\Checks\UsedDiskSpaceCheck;
use Spatie\Health\Facades\Health;

public function boot(): void
{
    Health::checks([
        DatabaseCheck::new(),
        RedisCheck::new(),
        QueueCheck::new(),
        UsedDiskSpaceCheck::new()->warnWhenUsedSpaceIsAbovePercentage(70),
    ]);
}

The package can store results, so your dependencies are not hit on every request to the endpoint.

Protecting the endpoint

A detailed health response tells anyone who reads it which services you run. Return a bare status to the public and the detail only to internal networks:

use Symfony\Component\HttpFoundation\IpUtils;

Route::get('/health', function () {
    if (! IpUtils::checkIp(request()->ip(), ['10.0.0.0/8', '192.168.0.0/16'])) {
        return response()->json(['status' => 'ok']);
    }

    // ... detailed checks
});

You can also rate limit the route with the throttle middleware so it cannot be used to load your database.

Practical rules

  • Keep it fast. Checks that take more than a second cause load balancer timeouts.
  • Never run migrations or heavy work in a health check.
  • Keep the route outside authentication middleware, so the load balancer can reach it.
  • If you run the app in containers, Docker can use the same endpoint. See Docker Compose healthchecks for healthcheck and depends_on conditions.

Health checks and uptime monitoring

A health check runs inside your infrastructure and tests internal components. An uptime monitor runs from outside and tests whether the app is reachable for users. Use both. Point an external monitor at the health endpoint every minute, so you also hear about it when the whole server is down. Monitoring and alerting in Laravel covers the monitor and the alerts it sends.