Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Laravel Health Checks and Uptime Monitoring

By Kokil Thapa | Last reviewed: September 2026

Your Laravel app can pass unit tests and still go down in production without anyone noticing. Laravel Health Checks and Uptime Monitoring close that gap by verifying database, cache, queue, and disk dependencies on a schedule—and paging you when the homepage stops responding. On production deployments I've maintained, a silent queue failure or expired SSL certificate caused more damage than a code bug. The same patterns apply whether you run Nagios-style server checks or Laravel-specific probes. This guide covers built-in Laravel options, Spatie's health package, custom endpoints, and external uptime tools you can wire up tonight.

What Are Laravel Health Checks and Why Do They Matter in Production?

A health check is a lightweight HTTP request that confirms your app and its dependencies are working. Uptime monitoring runs that request from outside your server on a fixed interval. Together they answer two different questions: "Can the app serve traffic right now?" and "Did something break five minutes ago while I was asleep?"

In my experience working on production Laravel applications, teams discover outages from customer complaints far too often. Booking portals, payment callbacks, and document upload workflows fail quietly when Redis dies or a queue worker stops. Health checks turn those silent failures into alerts you can act on before revenue or trust is lost.

Laravel Health Checks and Uptime MonitoringUptime MonitorExternal probeWeb ServerNginx / ApacheLaravel AppHealth endpointMySQLRedisQueueDiskAlert ChannelEmail, SMS, Slack, PagerDuty
Laravel Health Checks and Uptime Monitoring: external probes hit your app, which verifies dependencies and triggers alerts on failure.

Think of monitoring in three layers. Layer one is infrastructure: CPU, disk, and PHP-FPM process count. Layer two is application health: can Laravel connect to MySQL and Redis? Layer three is business flow: can a user complete checkout or upload a document? Most teams stop at layer one and wonder why the site "looks fine" while orders stop processing.

For Laravel 13.x on PHP 8.3 or higher—or Laravel 12 on PHP 8.2—you already have framework support for the second layer. You still need external uptime monitoring for the first signal when nginx misroutes traffic or a deploy leaves opcache serving stale bytecode.

How Do You Set Up the Built-In Laravel /up Health Route?

Laravel 11 and later ship a built-in health endpoint at /up. Laravel 13.x keeps this pattern. The route returns HTTP 200 when the framework boots successfully. It is a smoke test, not a full dependency audit, but it catches fatal errors, misconfigured .env files, and broken autoloaders immediately after deploy.

Confirm the route exists

Check bootstrap/app.php for the health registration:

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->create();

Hit it locally or on staging:

curl -i https://your-app.test/up

A healthy response returns 200 OK with a minimal body. Point your uptime monitor here first. It costs nothing and validates that PHP-FPM, the web server, and Laravel bootstrap all work together.

When /up is not enough

The built-in route does not verify MySQL, Redis, or queue workers. I've seen production apps return 200 on /up while every page requiring the database threw 500 errors because credentials rotated but opcache still held old config. Treat /up as a deploy gate, then add deeper checks for anything business-critical. Our Ubuntu server monitoring guide covers the infrastructure layer that complements this endpoint.

How Do You Build Custom Laravel Health Check Endpoints?

A custom endpoint gives you full control over what "healthy" means. Keep it fast, unauthenticated for the monitor IP range if needed, and separate from user-facing routes so traffic spikes do not skew results.

Create a dedicated health controller

Generate a controller and register a route outside heavy middleware:

php artisan make:controller HealthCheckController

Route::get('/health', HealthCheckController::class)
    ->middleware('throttle:60,1');

Example controller that checks database, cache, and disk:

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;

class HealthCheckController extends Controller
{
    public function __invoke()
    {
        $checks = [
            'database' => $this->checkDatabase(),
            'cache'    => $this->checkCache(),
            'disk'     => $this->checkDisk(),
        ];

        $healthy = ! in_array(false, $checks, true);

        return response()->json([
            'status' => $healthy ? 'ok' : 'fail',
            'checks' => $checks,
            'time'   => now()->toIso8601String(),
        ], $healthy ? 200 : 503);
    }

    private function checkDatabase(): bool
    {
        try {
            DB::connection()->getPdo();
            DB::select('SELECT 1');
            return true;
        } catch (\Throwable) {
            return false;
        }
    }

    private function checkCache(): bool
    {
        $key = 'health:'.uniqid();
        Cache::put($key, '1', 10);
        return Cache::get($key) === '1';
    }

    private function checkDisk(): bool
    {
        $free = disk_free_space(storage_path());
        $total = disk_total_space(storage_path());
        return ($free / $total) > 0.05;
    }
}

Return HTTP 503 when any check fails. Uptime tools treat non-2xx responses as down. Use JSON so you can inspect failures quickly—our JSON formatter helps when you paste monitor logs into tickets.

Protect the endpoint

Public health routes attract scanners. Options that work in practice:

  • Restrict by IP at the web server or firewall level.
  • Require a shared secret header: X-Health-Token checked in middleware.
  • Rate-limit aggressively; monitors need one request per minute, not thousands.
  • Never expose stack traces or internal hostnames in the JSON body.

On legal-tech portals and booking systems I've shipped, a failing health check during business hours beats discovering the problem from an angry client email at 10 PM.

Custom Health Check FlowMonitorGET /healthMiddlewareControllerDB probeCache probeQueue probeDisk probe200 JSON ok503 JSON fail
Custom Laravel health check flow: monitor request passes middleware, runs dependency probes, returns 200 or 503 JSON.

Should You Use Spatie Laravel Health or Roll Your Own?

Spatie Laravel Health is the package I reach for when a client wants a dashboard, scheduled checks, and notification hooks without maintaining custom probe code. It runs checks on a schedule via the Laravel scheduler and stores results you can expose on a secured route or Filament widget.

Install with Composer 2.10:

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

Register 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;

Health::checks([
    DatabaseCheck::new(),
    RedisCheck::new(),
    QueueCheck::new(),
    UsedDiskSpaceCheck::new()->warnWhenUsedSpaceIsAbovePercentage(80)
        ->failWhenUsedSpaceIsAbovePercentage(90),
]);

Schedule the runner in routes/console.php or your scheduler config:

Schedule::command('health:check')->everyMinute();

Spatie also ships queue and schedule heartbeat checks—critical for Laravel apps where php artisan queue:work runs under Supervisor and the scheduler runs from cron. A common mistake is monitoring only the homepage while jobs pile up unseen.

ApproachBest forProsCons
Built-in /upPost-deploy smoke testZero config, fastNo dependency checks
Custom /healthFull control, minimal depsTailored probes, simple JSONYou maintain all check logic
Spatie Laravel HealthTeams wanting dashboardsRich checks, notifications, historyExtra package + scheduler load
External uptime SaaS24/7 external perspectiveGlobal probes, SLA reportsCannot see internal queue depth alone

For a modular monolith or API-heavy app, combine Spatie scheduled checks with an external monitor on a public URL. See modern Laravel architecture best practices for how monitoring fits release discipline.

How Do You Monitor Queues, Scheduler, and Background Jobs?

HTTP uptime alone misses the most painful Laravel outages: queue workers that died after deploy and schedulers pointing at a stale release path. I've hit both on Deployer 7 symlink deployments where cron still referenced the previous release directory.

Queue worker heartbeat

Dispatch a lightweight job every minute from the scheduler. Write a timestamp to cache or a heartbeats table. Your health endpoint reads it and fails if the last heartbeat is older than three minutes.

Schedule::job(new QueueHeartbeatJob)->everyMinute();

public function checkQueueHeartbeat(): bool
{
    $last = Cache::get('queue:heartbeat');
    return $last && now()->diffInMinutes($last) < 3;
}

Run workers under Supervisor with autorestart. After each deploy, reload PHP-FPM and restart workers so they pick up new code. Our support and maintenance service includes exactly this kind of post-deploy checklist for production Laravel apps.

Scheduler verification

Laravel's scheduler needs a single cron entry:

* * * * * cd /var/www/app/current && php artisan schedule:run >> /dev/null 2>&1

Spatie's ScheduleCheck verifies the scheduler ran recently. Without it, nightly backups, report emails, and subscription renewals simply stop. On Adventure Third Pole Trek, booking reminders depend on the scheduler—monitoring it is not optional.

Failed job alerts

Wire Laravel's Queue::failing() callback or use Horizon's notification channels if you run Redis queues at scale. Pair this with Laravel notifications beyond email for SMS or Slack when payment or webhook jobs fail repeatedly.

Homepage vs Full Stack MonitoringHomepage OnlyGET / returns 200Queue worker deadJobs pile up silentlyCustomers see stale dataFalse green statusFull Stack ChecksHTTP + DB + RedisQueue heartbeat OKScheduler ran recentlyDisk below thresholdTrue production picture
Homepage-only uptime monitoring misses dead queue workers; Laravel Health Checks and Uptime Monitoring should cover background jobs too.

Which External Uptime Monitoring Tools Work Best with Laravel?

Application health endpoints must be polled from outside your server. If the datacenter network fails or nginx misconfigures SSL, an in-server cron job cannot tell you the site is unreachable from Kathmandu or Omaha.

SaaS uptime monitors

Services like UptimeRobot, Better Stack, Pingdom, and StatusCake hit your URL every 1–5 minutes from multiple regions. Configure them to:

  1. Monitor https://yourdomain.com/up for basic availability.
  2. Monitor https://yourdomain.com/health and assert JSON contains "status":"ok" where supported.
  3. Set alert thresholds: two consecutive failures before paging, response time warn above 2 seconds.
  4. Add SSL expiry checks—Let's Encrypt renewals fail silently when port 80 is blocked.

Free tiers cover small business sites. Paid tiers add on-call rotations and status pages. Budget roughly Rs 2,000–5,000/month (~USD 15–37) for a serious multi-site setup.

Self-hosted options

Uptime Kuma and Healthchecks.io run on your own VPS if you prefer not to send probe data to third parties. Pair self-hosted uptime with Prometheus and Grafana when you already export PHP-FPM and MySQL metrics. Our Linux system administration service covers installing and hardening these stacks on Ubuntu 22/24.

What to alert on

Prioritize alerts that map to user pain:

  • Homepage and login page HTTP status.
  • Custom health JSON returning 503.
  • SSL certificate expiry within 14 days.
  • Response time sustained above your p95 target.
  • Queue heartbeat stale for more than five minutes.
  • Disk usage above 90% on the volume holding storage/ and logs.

Use Laravel Telescope only in staging or behind strict auth—not as uptime monitoring. It helps after an alert fires, not before.

For API-first apps, add a lightweight authenticated ping route documented in your OpenAPI spec. See Laravel API best practices and building RESTful APIs with Laravel for versioning and health route conventions that play well with gateways.

Deploy and Monitor Checklist1. Deploy2. curl /up3. curl /health4. Restart queue5. OKExternal monitor confirms 200 from 3 regionsAll greendep rollback
Post-deploy Laravel Health Checks and Uptime Monitoring workflow: probe locally, confirm externally, restart workers, rollback on failure.

Database and Redis deep checks

Slow queries do not always fail health checks. Add optional timing thresholds: if SELECT 1 takes more than 500 ms, return a warning state. For PostgreSQL 18 or MySQL 9.7 backends, connection pool exhaustion shows up as intermittent timeouts first. Cross-read PostgreSQL for Laravel developers and N+1 query detection when response-time alerts fire without hard failures.

Official Laravel deployment guidance recommends verifying application health after each release. The Laravel 13.x deployment documentation covers optimization and the built-in health route in the release workflow context.

Key Takeaways

  • Enable Laravel's built-in /up route as your first post-deploy smoke test, then add a custom /health endpoint that returns 503 when dependencies fail.
  • Monitor queue workers and the scheduler with heartbeat checks—homepage uptime alone hides the failures that hurt eCommerce and booking apps most.
  • Use Spatie Laravel Health when you want scheduled checks, disk thresholds, and notification hooks without writing probe code from scratch.
  • Point an external uptime service at your health URL from multiple regions; assert JSON status where the tool supports content matching.
  • Protect health routes with IP allowlists or secret headers, rate limits, and zero internal detail in public responses.
  • Wire alerts to Slack or SMS for failed payment and webhook jobs, not just for HTTP downtime.

People Also Ask

Does Laravel have a built-in health check endpoint?

Yes. Laravel 11 and later register a /up route via bootstrap/app.php. It confirms the framework boots and returns HTTP 200. It does not test database, cache, or queue connectivity—you add those in a custom endpoint or with Spatie Laravel Health.

What HTTP status code should a failed Laravel health check return?

Return 503 Service Unavailable when any critical dependency fails. Uptime monitors treat non-2xx codes as down. Use 200 only when all required checks pass. Optional warnings can stay 200 with a degraded status in JSON if your monitor supports content assertions.

How often should you run Laravel uptime monitoring?

Probe every 1–5 minutes from at least two geographic regions. Business-critical apps warrant one-minute intervals with two-failure confirmation before alerting. Scheduled in-app checks via health:check can run every minute; align external polls similarly without exceeding rate limits.

Can you use Laravel Telescope for uptime monitoring?

No. Telescope is a debugging tool for requests, jobs, and queries in non-production or restricted environments. It does not replace external uptime monitoring or scheduled health checks. Use it to investigate after an alert, not as the alert source itself.

Ship Monitoring Before the Next Outage

Laravel Health Checks and Uptime Monitoring are cheap insurance compared to lost orders, missed booking confirmations, or a law-firm portal that silently stops accepting documents. Start tonight: confirm /up, build a JSON /health route, restart queue workers after deploy, and point an external monitor at both URLs. If you want this wired into your deploy pipeline on Laravel 13.x or need post-incident hardening on an existing app, see our enterprise application development and testing and optimization services—or review how we run production stacks on Quick And Easy Nepalese Grocery. Contact us to audit your current setup and get alerts working before the next silent failure.

Frequently Asked Questions

A health check is a lightweight HTTP request confirming your app and dependencies work. Uptime monitoring polls that endpoint from outside your server every 1–5 minutes and alerts you when checks fail.

Yes. Laravel 11 and later register a /up route via bootstrap/app.php with the health: '/up' option. Laravel 13.x keeps this pattern. It confirms the framework boots and returns HTTP 200, but does not verify MySQL, Redis, or queue connectivity. Point your external uptime monitor here first as a zero-config post-deploy smoke test. When credentials rotate or the database is unreachable, /up can still return 200 while user-facing pages fail—treat it as a deploy gate, not a full dependency audit.

Free tiers from UptimeRobot and similar services cover small business sites. Budget roughly Rs 2,000–5,000/month (~USD 15–37) for a serious multi-site setup with paid tiers, on-call rotations, and status pages.

Return 503 Service Unavailable when any critical dependency fails. Uptime monitors treat non-2xx responses as down. Use 200 only when all required checks pass.

Confirm bootstrap/app.php registers health: '/up' inside withRouting(). Hit it with curl -i https://your-app.test/up locally or on staging—a healthy response returns 200 OK with a minimal body. Point your external uptime monitor at this URL first. It validates that PHP-FPM, the web server, and Laravel bootstrap work together after each deploy. On Laravel 13.x with PHP 8.3 or higher, or Laravel 12 on PHP 8.2, this route ships by default. Add deeper checks separately for anything business-critical like payments or booking workflows.

The /up route does not verify MySQL, Redis, or queue workers. I've seen production apps return 200 on /up while every database-dependent page threw 500 errors because credentials rotated but opcache still held old config. Booking portals, payment callbacks, and document upload workflows fail quietly when Redis dies or a queue worker stops. Treat /up as a deploy gate, then add a custom /health endpoint or Spatie Laravel Health for dependency probes. Most teams stop at infrastructure monitoring and wonder why the site looks fine while orders stop processing.

Create a dedicated HealthCheckController and register Route::get('/health', HealthCheckController::class) with throttle:60,1 middleware. Probe database with DB::connection()->getPdo() and SELECT 1, verify cache with a write-read test, and check disk free space above 5% on storage_path(). Return JSON with status, checks, and timestamp—HTTP 200 when healthy, 503 when any check fails. Keep the endpoint fast, separate from heavy middleware, and unauthenticated only for your monitor IP range. On legal-tech portals and booking systems I've shipped, a failing health check during business hours beats discovering the problem from an angry client email at 10 PM.

Spatie Laravel Health suits teams wanting dashboards, scheduled checks, and notification hooks without maintaining custom probe code. Install with Composer 2.10 via composer require spatie/laravel-health, publish config, and register DatabaseCheck, RedisCheck, QueueCheck, and UsedDiskSpaceCheck in a service provider. Schedule health:check every minute. It also ships queue and schedule heartbeat checks—critical when php artisan queue:work runs under Supervisor. Roll your own custom /health endpoint when you want minimal dependencies and full control over probe logic. For API-heavy apps, combine Spatie scheduled checks with an external monitor on a public URL.

HTTP uptime alone misses dead queue workers and stale scheduler cron paths—I've hit both on Deployer 7 symlink deployments where cron still referenced the previous release directory. Dispatch a lightweight QueueHeartbeatJob every minute from the scheduler, write a timestamp to cache, and fail your health endpoint if the last heartbeat is older than three minutes. Run workers under Supervisor with autorestart and restart them after each deploy. Verify the scheduler with Spatie's ScheduleCheck and a single cron entry running schedule:run every minute. Wire Queue::failing() callbacks or Horizon notifications for repeated payment and webhook job failures.

SaaS services like UptimeRobot, Better Stack, Pingdom, and StatusCake poll your URL every 1–5 minutes from multiple regions. Configure them to monitor https://yourdomain.com/up for basic availability and https://yourdomain.com/health asserting JSON contains status:ok where supported. Set two consecutive failures before paging and warn when response time exceeds 2 seconds. Add SSL expiry checks—Let's Encrypt renewals fail silently when port 80 is blocked. Self-hosted alternatives include Uptime Kuma and Healthchecks.io on your own VPS. Application health endpoints must be polled from outside; an in-server cron cannot tell you the site is unreachable when nginx misconfigures SSL.

Public health routes attract scanners. Restrict by IP at the web server or firewall level so only your monitor's probe addresses can reach the endpoint. Require a shared secret header like X-Health-Token checked in middleware. Rate-limit aggressively—monitors need one request per minute, not thousands. Never expose stack traces or internal hostnames in the JSON body. Keep health routes separate from user-facing routes so traffic spikes do not skew uptime results. Use Laravel Telescope only in staging or behind strict auth for debugging after an alert fires, not as uptime monitoring itself.

Most teams stop at layer-one infrastructure monitoring and wonder why the site looks fine while orders stop processing. Homepage-only uptime monitoring misses dead queue workers, stale schedulers, and silent Redis failures. Booking reminders, payment callbacks, nightly backups, and subscription renewals depend on background jobs and cron—not HTTP responses. Think of monitoring in three layers: infrastructure CPU and disk, application health for MySQL and Redis connectivity, and business flow for checkout or document upload. Laravel Health Checks and Uptime Monitoring should cover background jobs too, not just whether the homepage returns 200.

Prioritize alerts that map to user pain: homepage and login page HTTP status, custom health JSON returning 503, SSL certificate expiry within 14 days, and response time sustained above your p95 target. Add queue heartbeat stale for more than five minutes and disk usage above 90% on the volume holding storage/ and logs. Wire alerts to Slack or SMS for failed payment and webhook jobs, not just HTTP downtime. For database backends on PostgreSQL 18 or MySQL 9.7, optional timing thresholds can warn when SELECT 1 takes more than 500 ms before hard failures appear.

After each deploy, probe /up locally first to confirm PHP-FPM, the web server, and Laravel bootstrap work together. Confirm the same endpoint externally from your uptime service—internal checks cannot detect nginx misroutes or SSL misconfiguration. Reload PHP-FPM to invalidate opcache, then restart queue workers under Supervisor so they pick up new code. Verify the scheduler cron entry points at the current release path, not a stale Deployer symlink target. Roll back immediately if /health returns 503 or external probes fail twice consecutively. Official Laravel deployment guidance recommends verifying application health after each release.

Health checks answer whether your app and its dependencies—database, cache, queue, disk—can serve traffic right now. Uptime monitoring runs those checks from outside your server on a fixed 1–5 minute interval and pages you when something broke while you were asleep. Together they cover two gaps: an in-app /health endpoint catches Redis dying or queue workers stopping, while external probes catch datacenter network failures, expired SSL certificates, and nginx misconfigurations your server cannot detect from inside. Layer-one infrastructure checks alone miss the silent failures that hurt eCommerce and booking apps most.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: