ukfast/laravel-health-check
Laravel health check package that exposes a configurable endpoint for monitoring app health. Run checks on database, cache, queue, storage, Redis and custom services, returning JSON output and proper HTTP status codes for load balancers, Kubernetes probes and uptime monitors.
ans-group/laravel-health-check gives you a single, structured health endpoint that aggregates named checks (database, cache, redis, disk space, HTTP dependencies, custom probes) and returns a machine-readable payload for load balancers, uptime monitors and orchestrators.
Version floor for v3.0.0 — before you composer require, confirm your app satisfies:
>= 8.2>= 12.0 (Laravel 13 is now supported)composer require ans-group/laravel-health-check
# publish the config so you can see the route + default check list
php artisan vendor:publish --tag="health-check-config"
Then register the endpoint from a service provider (AppServiceProvider::boot() is fine for a single route):
use AnsGroup\HealthCheck\Facades\HealthCheck;
use AnsGroup\HealthCheck\Checks\CacheCheck;
use AnsGroup\HealthCheck\Checks\DatabaseCheck;
HealthCheck::checks([
new CacheCheck(),
new DatabaseCheck(),
])->route('/health');
First use case: hit GET /health locally and wire the same URL into your uptime monitor / container liveness probe. Start with only hard dependencies (DB, cache); add network-dependent checks after you've decided on timeouts and caching. The bundled artisan command (php artisan health:check) is the quickest way to see individual check output without parsing JSON.
Group checks by endpoint, not by "everything". A liveness probe should be cheap and dependency-free; a readiness probe should verify DB, cache and queue connectivity. Register two routes with different check sets:
HealthCheck::checks([new CacheCheck()])->route('/health/live');
HealthCheck::checks([new DatabaseCheck(), new CacheCheck(), new RedisCheck()])->route('/health/ready');
Custom checks are the main extension point. Implement the package's Check contract — expose name() and run() returning a result object with an ok/fail flag, message and optional structured data (latency, version numbers, queue depth). Keep run() free of side effects and make it observable — a monitor can only tell you "horizon down" if you include the reason in the message.
class HorizonCheck implements Check
{
public function name(): string
{
return 'horizon';
}
public function run(): CheckResult
{
$supervisors = Horizon::supervisors();
return count($supervisors) > 0
? CheckResult::ok()->withData(['supervisors' => count($supervisors)])
: CheckResult::fail()->withMessage('No Horizon supervisors running');
}
}
Treat checks as config, not code, where possible. The published config/health-check.php lets you toggle the default check list per environment — enable Redis only in staging/production, disable external HTTP checks in CI, and keep the route definition identical across environments.
Integrate at the edge, not just in monitoring. The most common day-to-day uses are: container livenessProbe/readinessProbe, ALB/ELB target group health checks, Forge/Vapor deployment checks, and post-deploy smoke tests in CI (curl -fsS https://app.test/health | jq -e '.status == "ok"'). Because the payload is structured, you can assert on individual checks rather than the aggregate status.
Guard the endpoint. If the payload exposes service names, versions or connection details, put the readiness route behind auth or IP allow-listing via route middleware, and keep a separate anonymous, information-poor liveness route for the platform.
v3.0.0 is a breaking release, but only at the platform level. No check API or config format changes are described — the breaks are entirely composer.json constraints. That means your upgrade risk is concentrated in CI, not in application code:
php to >= 8.2 in your root composer.json and align any Docker base images / platform.php config.composer why-not laravel/framework 12.*.^2.1|^3.0 rather than dropping v2, or you'll force consumers into a simultaneous major bump.shivammathur/setup-php PHP versions, laravel/framework test versions) in the same PR — the failure mode otherwise is "works locally, matrix still tests 8.1".orchestra/testbench, you need the testbench line that targets Laravel 12/13; an old testbench will fail before any of your own code runs.Don't let the health endpoint become the outage. Any check that performs I/O can hang. Give outbound HTTP/Redis checks an explicit short timeout, and cache expensive results for a few seconds so a 5-second probe interval doesn't run a full DB + Redis sweep 12 times a minute. A health endpoint that times out is indistinguishable from a dead app.
A failing check must not 500 the whole route. The package is designed to report failures as a non-OK status in the payload; if you wrap checks in your own try/catch, re-throw as a CheckResult::fail() with the exception message rather than letting it bubble — otherwise your monitor gets a generic 500 with no diagnosis.
Beware environment-specific check lists. Database checks against a read replica, queue checks in a sync-driver local environment, and cache checks when CACHE_DRIVER=array all produce spurious failures. Gate checks on config values or add environment guards inside the check itself.
Route caching and middleware. If you route:cache, remember the health route is registered programmatically in a service provider — verify it survives php artisan optimize in your deploy pipeline. Also confirm CSRF/session middleware isn't applied to the probe route; a monitor with no cookie jar will get a 419 instead of a health result.
Debugging in tests. Running the endpoint test-suite with cached health results enabled will give you stale assertions — disable result caching in phpunit.xml or the testing environment, and assert on the individual check keys rather than the aggregate status so a failure tells you which dependency broke.
Extension points worth knowing: the Check contract for new probes, the published config for default check sets, the route registration API for path/middleware control, and the artisan command for local triage. Between those four you can cover almost every day-to-day requirement without touching package internals.
How can I help you explore Laravel packages today?