jtkalkman/laravel-health

Configurable Laravel health check endpoint with Semonto-compatible JSON output

Maintainers

Package info

github.com/JTKalkman/laravel-health

pkg:composer/jtkalkman/laravel-health

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-06-07 09:30 UTC

This package is auto-updated.

Last update: 2026-06-07 09:32:56 UTC


README

A lightweight, configurable Laravel package that exposes a health check endpoint with Semonto server health monitoring compatible JSON output.

No GUI, just a clean JSON endpoint that tells you and your monitoring tools whether your server is healthy.

Requirements

  • PHP 8.3+
  • Laravel 12 or 13

Installation

composer require jtkalkman/laravel-health

Publish the configuration file:

php artisan vendor:publish --tag=health-config

Configuration

The published config/health.php is the single place to configure everything, which checks to run, thresholds, authentication, caching, and the endpoint route.

Example config/health.php

<?php

use JTKalkman\LaravelHealth\HealthChecks\CpuLoadCheck;
use JTKalkman\LaravelHealth\HealthChecks\DatabaseConnectionCheck;
use JTKalkman\LaravelHealth\HealthChecks\DatabaseConnectionCountCheck;
use JTKalkman\LaravelHealth\HealthChecks\DiskSpaceCheck;
use JTKalkman\LaravelHealth\HealthChecks\DiskSpaceInodeCheck;
use JTKalkman\LaravelHealth\HealthChecks\MemoryCheck;

return [

    /*
    |--------------------------------------------------------------------------
    | Health Check Route
    |--------------------------------------------------------------------------
    |
    | The URI where the health endpoint will be accessible.
    |
    */
    'route' => env('HEALTH_ROUTE', '/health'),

    /*
    |--------------------------------------------------------------------------
    | Authentication
    |--------------------------------------------------------------------------
    |
    | The header name and value your monitoring tool sends with each request.
    | Required, the endpoint will return 404 until this is set.
    |
    | When using Semonto, set the header name to match what you configured
    | in your Semonto server health monitoring settings.
    |
    */
    'auth' => [
        'health_check_header_name' => env('HEALTH_CHECK_HEADER_NAME', 'health-monitor-access-key'),
        'health_check_secret'      => env('HEALTH_CHECK_SECRET', null),
    ],

    /*
    |--------------------------------------------------------------------------
    | HTTPS
    |--------------------------------------------------------------------------
    |
    | When enabled, the health endpoint will only respond to HTTPS requests.
    | Strongly recommended in production. Set to false for local development.
    |
    */
    'require_https' => env('HEALTH_REQUIRE_HTTPS', true),

    /*
    |--------------------------------------------------------------------------
    | Cache
    |--------------------------------------------------------------------------
    |
    | Results are cached to prevent the health endpoint itself from becoming
    | a source of load on your server. Set to 0 to disable caching.
    |
    */
    'cache_ttl' => env('HEALTH_CACHE_TTL', 30),

    /*
    |--------------------------------------------------------------------------
    | Checks
    |--------------------------------------------------------------------------
    |
    | Register the health checks to run.
    |
    | Run `df -P` and `df -iP` on your server to list available mount points.
    |
    */
    'checks' => [
        [DiskSpaceCheck::class, ['path' => '/', 'warningThreshold' => 75, 'errorThreshold' => 90]],
        [DiskSpaceInodeCheck::class, ['path' => '/', 'warningThreshold' => 75, 'errorThreshold' => 90]],
        [MemoryCheck::class, ['warningThreshold' => 75, 'errorThreshold' => 90]],
        [CpuLoadCheck::class, ['minutes' => 1,  'warningThreshold' => 70, 'errorThreshold' => 90]],
        [CpuLoadCheck::class, ['minutes' => 5,  'warningThreshold' => 60, 'errorThreshold' => 80]],
        [CpuLoadCheck::class, ['minutes' => 15, 'warningThreshold' => 50, 'errorThreshold' => 70]],
        [DatabaseConnectionCheck::class, ['connection' => 'mysql']],
        [DatabaseConnectionCountCheck::class, ['connection' => 'mysql', 'warningThreshold' => 75, 'errorThreshold' => 90]],
    ],

];

Environment variables

Add these to your .env:

HEALTH_CHECK_SECRET=your-secret-key
HEALTH_REQUIRE_HTTPS=true

# Optional overrides
HEALTH_ROUTE=/health
HEALTH_CHECK_HEADER_NAME=health-monitor-access-key
HEALTH_CACHE_TTL=30

For local development:

HEALTH_REQUIRE_HTTPS=false
HEALTH_CHECK_SECRET=my-test-secret

Finding your mount points

Run the following commands on your server to list available mount points before configuring disk checks:

# Disk space
df -P

# Inode usage
df -iP

Add a DiskSpaceCheck and/or DiskSpaceInodeCheck for each mount point you want to monitor.

Built-in checks

Check Description value
DiskSpaceCheck Disk space usage for a mount point % used
DiskSpaceInodeCheck Inode usage for a mount point % used
MemoryCheck System memory usage % used
CpuLoadCheck CPU load average (1, 5, or 15 minutes) as % of total capacity % of capacity
DatabaseConnectionCheck Database connection and query response time seconds
DatabaseConnectionCountCheck Active DB connections as % of max (MySQL/MariaDB/PostgreSQL) % used

CPU load and core count

CPU load thresholds are expressed as a percentage of total CPU capacity. A load of 1.0 per core equals 100% utilisation, so on a 4-core server, a load of 4.0 is 100%. The package detects your core count automatically, so you configure thresholds as percentages regardless of the server's core count:

// Conservative thresholds, sustained load is more concerning than spikes
fn() => new CpuLoadCheck(minutes: 1,  warningThreshold: 70, errorThreshold: 90),  // spikes OK
fn() => new CpuLoadCheck(minutes: 5,  warningThreshold: 60, errorThreshold: 80),
fn() => new CpuLoadCheck(minutes: 15, warningThreshold: 50, errorThreshold: 70),  // sustained load

Multiple database connections

Laravel supports multiple database connections. Add a check for each one you want to monitor:

fn() => new DatabaseConnectionCheck(connection: 'mysql'),
fn() => new DatabaseConnectionCheck(connection: 'pgsql'),
fn() => new DatabaseConnectionCheck(connection: 'tenant'),

Custom checks

Extend the HealthCheck abstract class anywhere in your application, no need to touch the vendor folder:

<?php

namespace App\HealthChecks;

use JTKalkman\LaravelHealth\HealthChecks\HealthCheck;
use JTKalkman\LaravelHealth\HealthCheckResult;
use JTKalkman\LaravelHealth\HealthCheckStatus;

class RedisCheck extends HealthCheck
{
    protected string $name = 'Redis ping';

    protected function performHealthCheck(): HealthCheckResult
    {
        $start = microtime(true);
        \Illuminate\Support\Facades\Redis::ping();
        $duration = round(microtime(true) - $start, 3);

        return new HealthCheckResult(
            name: $this->name,
            status: HealthCheckStatus::OK->value,
            value: $duration,
            description: "Redis responded in {$duration}s.",
        );
    }
}

Register it in config/health.php:

'checks' => [
    // ...
    fn() => new \App\HealthChecks\RedisCheck(),
],

The abstract class handles all error cases, if performHealthCheck() throws for any reason, the check returns an error result automatically. Your check only needs to handle the happy path.

JSON response

The endpoint returns a JSON response compatible with Semonto server health monitoring:

{
    "results": [
        {
            "name": "Disk space /",
            "description": "/ 9% used.",
            "value": 9,
            "status": "ok"
        },
        {
            "name": "Memory usage",
            "description": "19.6% used.",
            "value": 19.6,
            "status": "ok"
        }
    ],
    "status": "ok"
}

Status values

Status Meaning
ok Check passed
warning Check passed with warnings
error Check failed or could not run

The top-level status reflects the worst status across all checks. The HTTP response code is 200 for ok and 503 for anything else.

Security

  • The endpoint returns 404 for any request that fails authentication or HTTPS requirements, no information is leaked about the endpoint's existence
  • Secret key comparison uses hash_equals() to prevent timing attacks
  • Results are cached to prevent the endpoint from being used as a DoS vector
  • Always use HTTPS in production, the secret key travels in a request header

Semonto compatibility

This package produces a standard JSON health endpoint that is compatible with Semonto server health monitoring. The endpoint can also be consumed by any monitoring tool, custom dashboard, or deployment pipeline that can make an HTTP request and parse JSON.

To connect Semonto to your health endpoint, add the endpoint URL in your Semonto server health monitoring settings and configure an access key. Set the same key as HEALTH_CHECK_SECRET in your .env and make sure HEALTH_CHECK_HEADER_NAME matches the header name configured in Semonto, which defaults to health-monitor-access-key.

License

MIT License

Copyright (c) 2026 J.T. Kalkman