Laravel Health Checks and Uptime Monitoring

Khimananda Oli 9 min read DevOps
Laravel Health Checks and Uptime Monitoring

By Khimananda Oli | Last reviewed: August 2026

Your application returns HTTP 200, but users cannot log in because the database connection pool is exhausted or the queue worker died silently. Relying solely on basic server ping is insufficient for modern PHP applications; you need comprehensive Laravel health checks and uptime monitoring that validate actual business functionality, not just network reachability. This guide covers implementing internal self-checks via Spatie Health alongside external synthetic monitoring to ensure your stack is genuinely operational.

Before configuring application-level diagnostics, ensure your foundation is solid. A misconfigured web server will render even the best health check code useless. Review my guide on deploying Laravel on Ubuntu VPS with Nginx to confirm your PHP-FPM and reverse proxy are correctly tuned for production traffic.

Laravel AppSpatie Health/health-checkDB CheckCache CheckQueue CheckExternal MonitorUptime Robot / Better StackSSL Expiry ProbeMulti-region Ping
Two-layer Laravel health checks and uptime monitoring architecture combining internal Spatie Health dependency validation with external synthetic probes.

How Do You Configure Spatie Health for Laravel Health Checks?

The Spatie Health package is the de facto standard for internal application diagnostics in the Laravel ecosystem. It provides a structured way to verify that every critical dependency your application needs is actually functional. Installation is straightforward via Composer, but configuration determines whether it becomes a useful safety net or noisy clutter.

composer require spatie/laravel-health
php artisan vendor:publish --tag="health-config"

After publishing, edit config/health.php to register checks relevant to your stack. Avoid enabling every available check; only monitor dependencies that cause immediate user-facing failure when down. A common mistake in Nepal-based deployments is forgetting to add Redis or Memcached checks when using file-based cache locally but Redis in production.

Registering Critical Dependency Checks

  • DatabaseCheck: Verifies PDO connectivity and runs a simple query. Essential for RDS/Aurora failover detection.
  • CacheCheck: Confirms the configured cache driver can write and read. Catches Redis auth failures or Memcached evictions.
  • QueueCheck: Validates that at least one queue worker has processed a job recently. Uses the queue-monitor table or heartbeat mechanism.
  • SslCertificateCheck: Monitors certificate expiry for your primary domain and any SANs. Prevents surprise outages during renewal windows.
  • ScheduleCheck: Ensures cron jobs are executing on time. Critical if you rely on scheduled reports, cleanup tasks, or billing cycles.
// app/Providers/AppServiceProvider.php
use Spatie\Health\Facades\Health;
use Spatie\Health\Checks\Checks\DatabaseCheck;
use Spatie\Health\Checks\Checks\CacheCheck;
use Spatie\Health\Checks\Checks\QueueCheck;
use Spatie\Health\Checks\Checks\SslCertificateCheck;

public function boot(): void
{
    Health::checks([
        DatabaseCheck::new()
            ->connection('mysql')
            ->failWhenDatabaseIsReadOnly(),
        CacheCheck::new()
            ->driver('redis'),
        QueueCheck::new()
            ->queues(['default', 'emails'])
            ->failWhenNoJobRanInLastMinutes(5),
        SslCertificateCheck::new()
            ->url('https://yourdomain.com')
            ->failWhenCertificateExpiresWithinDays(14),
    ]);
}

Expose these checks via a dedicated route. Always protect this endpoint; unauthenticated health endpoints leak infrastructure details and can be abused for reconnaissance. Use middleware to restrict access to internal IPs or require a bearer token.

// routes/web.php
use Spatie\Health\Http\Controllers\HealthCheckJsonController;

Route::get('/health-check', HealthCheckJsonController::class)
    ->middleware(['auth:sanctum']) // Or custom IP whitelist middleware
    ->name('health.check');

What Is the Difference Between Internal Health Checks and External Uptime Monitoring?

Internal health checks and external uptime monitoring serve fundamentally different purposes, and conflating them creates dangerous blind spots. Understanding this distinction is critical for building resilient systems, especially when managing infrastructure across regions or dealing with Nepal's occasional ISP routing instability.

CriteriaInternal Health Checks (Spatie)External Uptime Monitoring
PerspectiveInside the application container/serverOutside, from global PoPs
DetectsDB down, cache miss, queue stall, OOMDNS failure, CDN outage, firewall block, SSL error
Blind SpotCannot detect network partition or DNS hijackCannot detect internal state corruption
Latency SensitivityNear-instant (local call)Includes network round-trip + TLS handshake
Alert TriggerDependency failure threshold breachedHTTP status ≠ 200 or response time > SLA
Best ToolSpatie Health, Laravel PulseBetter Stack, UptimeRobot, Pingdom

In practice, I configure external monitors to hit both the public homepage and the authenticated /health-check endpoint. The homepage confirms end-user accessibility through Cloudflare or AWS CloudFront, while the health endpoint validates backend integrity. If the homepage fails but the health endpoint succeeds, you have a CDN or WAF issue. If the health endpoint fails but the homepage loads, your database or cache is degraded but static assets are still served.

Alert TriggeredExternal Monitor Status?FAILOKNetwork / CDN IssueCheck DNS, Firewall, EdgeInternal DegradationCheck DB, Cache, QueueVerify Edge ConfigInspect Spatie Output
Alert triage flow for Laravel health checks and uptime monitoring: correlate external probe status with internal check results to isolate root cause.

How Do You Monitor Laravel Queues and Scheduled Tasks Reliably?

Queue workers and scheduled tasks are the most common silent failure points in Laravel applications. A health check returning 200 means nothing if your email queue has been stuck for three hours or your nightly invoice generation never ran. Traditional uptime monitors cannot see inside your application's asynchronous processing layer.

Implementing Queue Heartbeats

Spatie Health’s QueueCheck relies on a heartbeat mechanism. Workers must periodically update a timestamp in the database or cache. Configure your supervisor or systemd service to run the heartbeat command alongside your worker:

# In your supervisor config or deployment script
php artisan queue:work --tries=3 --max-time=3600 &
php artisan health:queue-heartbeat --every-minute

For teams using Laravel Horizon, leverage its built-in metrics instead of duplicating heartbeats. Horizon already tracks job throughput, wait times, and failed jobs. Create a custom Spatie check that queries Horizon’s Redis keys directly rather than maintaining parallel tracking logic.

Scheduled Task Verification

The ScheduleCheck verifies that schedule:run executed within the expected window. However, this only confirms the scheduler itself ran—not that individual tasks completed successfully. For critical business tasks (billing, data sync, compliance reports), implement task-specific completion markers:

// In your scheduled task
Schedule::call(function () {
    // Your critical business logic here
    generateMonthlyInvoices();
    
    // Mark completion with timestamp
    Cache::put('health:last_invoice_run', now(), now()->addHours(26));
})->monthlyOn(1, '02:00');

// Custom Spatie check
class InvoiceGenerationCheck extends Check
{
    public function run(): Result
    {
        $lastRun = Cache::get('health:last_invoice_run');
        
        if (!$lastRun || now()->diffInHours($lastRun) > 26) {
            return Result::failed('Invoice generation overdue');
        }
        
        return Result::ok();
    }
}

This pattern gives you granular visibility into business-critical processes beyond generic scheduler health. When auditing for SOC 2 or ISO 27001 compliance, these task-specific markers serve as automated evidence of control execution—something I frequently help Nepali fintech companies implement during audit preparation.

Which External Uptime Monitoring Service Works Best for Laravel Apps?

Choosing an external monitoring provider depends on your latency requirements, budget, and integration needs. While many services offer basic HTTP checks, Laravel applications benefit from platforms supporting multi-step transactions, keyword assertions, and webhook integrations with your existing alerting stack.

Better Stack✓ Multi-step API✓ Keyword Assert✓ Webhook + Slack✓ Status Page Built-in✓ Incident MgmtBest for TeamsUptimeRobot✓ Basic HTTP/TCP✗ No Multi-step✓ Free Tier Generous△ Status Page Addon✗ Limited IntegrationsBest for Solo/SmallPingdom✓ Transaction Monitor✓ Real Browser✓ Advanced Reports✓ Status Page Included✓ Enterprise SLABest for Enterprise$29+/moFree / $7+/mo$10+/mo
Feature comparison of external uptime monitoring providers for Laravel applications across team size and capability tiers.

For most Laravel projects serving Nepali and global audiences, I recommend Better Stack (formerly Better Uptime). Its multi-step API monitoring lets you simulate actual user login flows rather than just pinging endpoints. The built-in status page integrates cleanly with Spatie Health output, and their incident management workflow aligns well with on-call rotations. If budget is constrained and you only need basic HTTP checks, UptimeRobot’s free tier covers up to 50 monitors with 5-minute intervals—adequate for staging environments or low-traffic sites.

Configure your external monitor to assert specific content in the response body, not just HTTP status codes. A Laravel app might return 200 while displaying a database error page due to misconfigured exception handling. Set keyword assertions for unique strings like your app name or a known UI element to confirm genuine page rendering.

Securing Your Laravel Health Checks and Uptime Monitoring Endpoints

Exposing health check endpoints without proper authentication is a security anti-pattern. Attackers enumerate infrastructure details, identify vulnerable dependencies, and map internal service topology through unprotected health routes. Even if your app isn’t high-value, automated scanners will find and exploit exposed endpoints.

Implement defense-in-depth for your health check route:

  1. IP Whitelisting: Restrict access to known monitoring service IPs. Better Stack, UptimeRobot, and Pingdom publish their probe IP ranges. Update these quarterly as providers rotate infrastructure.
  2. Bearer Token Authentication: Require a strong, rotated secret token passed via header. Store it in environment variables, never in code. Rotate every 90 days as part of your secrets management hygiene—if you’re using Vault or AWS Secrets Manager, automate rotation entirely.
  3. Rate Limiting: Apply strict rate limits to prevent abuse. Health checks should be called no more than once per minute per source IP.
  4. Response Sanitization: In production, return minimal JSON. Exclude stack traces, version numbers, and internal hostnames. Save detailed output for authenticated admin panels or logging systems only.
// Middleware example for IP + token validation
class SecureHealthCheck
{
    public function handle(Request $request, Closure $next)
    {
        $allowedIps = config('health.allowed_ips', []);
        $token = config('health.auth_token');
        
        if (!in_array($request->ip(), $allowedIps)) {
            abort(403);
        }
        
        if ($request->header('X-Health-Token') !== $token) {
            abort(401);
        }
        
        return $next($request);
    }
}

This layered approach ensures your Laravel health checks and uptime monitoring infrastructure doesn’t become an attack vector itself. During SOC 2 audits, I consistently see findings related to exposed diagnostic endpoints—implementing these controls upfront prevents remediation costs later.

Next Steps for Production-Ready Laravel Monitoring

Laravel health checks and uptime monitoring form the observability foundation for any serious PHP application. Start by deploying Spatie Health with database, cache, and queue checks behind authenticated routes. Pair this with external synthetic monitoring from Better Stack or UptimeRobot to catch network-layer failures your app cannot see internally. Test your alerting paths quarterly—simulate failures during maintenance windows to verify notifications reach the right people at the right time.

If you’re preparing for compliance audits or scaling infrastructure across regions, consider integrating these health signals into your broader observability stack. My guide on setting up Prometheus and Grafana shows how to ingest Spatie Health metrics for trend analysis and capacity planning. For teams managing complex deployments, zero-downtime deployment strategies pair naturally with health-gated release pipelines.

Need help designing a monitoring strategy tailored to your Laravel application’s architecture and compliance requirements? Reach out to discuss your specific setup.

Frequently Asked Questions

Spatie Laravel Health remains the standard in 2026. It provides built-in checkers for database, cache, queue, and storage while integrating directly with Laravel's scheduling system for automated monitoring.

Run composer require spatie/laravel-health then publish the config file using artisan vendor:publish. Register the service provider if not auto-discovered and define your checks in the configuration array.

Yes. Use the QueueCheck to verify jobs are processing within acceptable delays. Configure a threshold in seconds to detect stuck workers or failed connections before they impact users.

Minimal impact when scheduled correctly. Run checks via cron every minute rather than on web requests. Cache results briefly to prevent redundant database queries during high-traffic periods or status page refreshes.

Create a dedicated route returning JSON status. Protect it with IP whitelisting or bearer token authentication to prevent public exposure of internal infrastructure details and potential attack surface enumeration.

UptimeRobot, Better Stack, and Grafana Synthetic all support custom HTTP checks. Point them to your secured health endpoint and configure alerting thresholds based on response time and status code expectations.

Schedule checks every minute via Laravel Scheduler. External uptime monitors should poll every thirty to sixty seconds. Adjust frequency based on SLA requirements and acceptable detection latency for production incidents.

Yes. Implement listeners on check failures to restart queues, clear caches, or notify on-call engineers. Avoid aggressive auto-remediation without human confirmation to prevent cascading failures during partial outages.

Verify connectivity, replication lag, and slow query counts. Check connection pool utilization against max_connections limits. Monitor disk usage percentages to prevent write failures during peak load or unexpected data growth spikes.

Use artisan health:check to run all registered checks manually. Mock external services in tests to verify failure handling. Validate JSON output structure matches what your uptime monitor expects.

Yes. Self-host Uptime Kuma or use Prometheus Blackbox Exporter. Both support HTTP health endpoints and provide alerting via webhook, email, or Slack without recurring subscription costs for small teams.

Implement retry logic with exponential backoff before alerting. Require consecutive failures across multiple check intervals. Add grace periods for known maintenance windows and correlate alerts with deployment events to reduce noise.

Only if critical to core functionality. Mark external API checks as non-blocking to prevent cascading failures. Set longer timeouts and separate alerting channels to distinguish internal issues from vendor outages.

The check fails and returns degraded status. Configure timeout values per check to match expected response times. Long-running checks should run asynchronously to prevent blocking the scheduler or other checks.

Store check definitions in config files committed to git. Use environment variables for thresholds and credentials. Review changes during code review to ensure monitoring coverage matches new features or infrastructure changes.