Laravel powers a huge range of production apps, from marketing sites and internal dashboards to high-volume APIs. But a Laravel deployment is far more than one PHP process answering requests. It is php-fpm or Octane workers, a database, a cache, a queue connection with workers draining it, a scheduler firing periodic commands, and static assets built by Vite. Any one of those can fail while the rest keep serving traffic, which means "is the homepage up?" tells you almost nothing about whether the application actually works.

This guide walks through monitoring a Laravel app the way it actually breaks. We will use the built-in health route, enumerate the Laravel-specific failure modes worth alerting on, cover queues and the scheduler with heartbeats, and finish with a concrete CronAlert setup you can copy. CronAlert is agentless and runs on Cloudflare's edge, so everything here is HTTP checks, content assertions, and heartbeats — nothing to install inside your app server.

Start with the /up health route

Laravel 11 and newer ship a built-in health check route at /up out of the box. It is registered in bootstrap/app.php via the application configuration, boots the framework, dispatches a DiagnosingHealth event, and returns HTTP 200 when the app is healthy. That gives you a ready-made URL whose status code is a faithful signal of whether the framework itself can serve a request — point a monitor at it and you are already ahead of most deployments. For the broader pattern, see our guide to HTTP health check endpoints.

The catch is that the default /up route confirms the framework boots but does not verify your dependencies. A database that is unreachable, a queue connection that is down, or a cache that has vanished will not, by themselves, make the default health route fail. For a signal you can actually trust, make the check dependency-aware. You have two clean options. The first is to listen for the DiagnosingHealth event and throw an exception when a critical dependency is unhealthy — a trivial query, a quick cache set-and-get, and a check that the queue connection responds. The second is spatie/laravel-health, which ships configurable checks for the database, cache, queue, Redis, disk space, scheduled tasks, and more, and exposes its own endpoint you can monitor. Our deep-dive on building a database health endpoint covers the query-level details and the traps to avoid.

Three rules keep the endpoint useful. Keep it fast — no expensive aggregations or slow external calls, or the health check becomes the outage. Keep the response small and free of sensitive data. And decide on authentication deliberately: a status-code-only endpoint is safe to leave public, while a richer diagnostic endpoint should sit behind a static token in a header or query string so your monitor can still reach it.

What to monitor in a Laravel app

One health route is the foundation, not the whole story. A complete picture spreads a handful of checks across the surfaces your users and integrators actually touch:

  • The public site and homepage. The page real visitors land on, checked for a 200 and for real rendered content — not just that something responded.
  • The health route. Your /up or dependency-aware endpoint, giving you a signal independent of any single page.
  • Key API routes. If you serve an API, your most important read endpoints deserve their own checks; an API can break while the HTML site is fine. See monitoring API endpoints for assertions on JSON responses.
  • The login page. Your authentication entry point is an operational lifeline; if it stops rendering, you have lost the door into your own app.
  • Static and asset availability. A direct request to a known Vite-built CSS or JS URL confirms the build ran and your storage or CDN is serving assets.
  • HTTPS and the SSL certificate. An expired certificate takes the whole site down for browsers regardless of how healthy the app is.

You can cover all of this comfortably inside CronAlert's free plan, which includes 25 monitors at a 3-minute interval with SSL and content checks. Upgrading to Pro tightens the interval to 1 minute and unlocks keyword monitoring across every monitor.

Laravel-specific failure modes

Generic uptime advice misses the things that actually take Laravel apps down. These are the failures worth designing your checks around:

  • Dead queue workers. When the worker pool crashes or is never restarted after a deploy, queued jobs pile up in the connection and silently stop completing. Emails do not send, exports do not generate, and HTTP traffic carries on as if nothing is wrong.
  • The scheduler not firing. Laravel's scheduler depends on a single system cron entry running php artisan schedule:run every minute. If that cron line is missing or the container it lives in is not running, every scheduled task silently stops — the classic "did my Laravel scheduler actually run?" problem.
  • Maintenance mode left on. Running php artisan down returns HTTP 503 for every request, and it is easy to leave enabled after a deploy that failed partway through.
  • Migrations not run. A deploy that ships new models but skips php artisan migrate throws query exceptions on any route that touches the changed tables — often a 500 on a subset of pages while the homepage looks fine.
  • APP_DEBUG=true in production. A misconfigured deploy leaking APP_DEBUG=true exposes Whoops stack traces, environment variables, and secrets to the world. It is a security incident that still returns 200, so only a content check will flag it.
  • php-fpm or Octane worker exhaustion. Under load, or with a slow route, the worker pool runs out of available workers and requests queue, time out, and surface as intermittent 502s or 504s with climbing response times.
  • Redis or cache unavailable. If Redis backs your cache, sessions, or queue and it becomes unreachable, large parts of the app break even though php-fpm is perfectly healthy.
  • Storage and permission issues. A storage/ directory that is not writable — bad permissions after a deploy, or a full disk — breaks logging, sessions, and file caching in ways that produce 500s from otherwise-fine code.

Notice how many of these never change the homepage status code. That is the central lesson of Laravel monitoring: status codes alone lie, and you need content assertions, dependency-aware health checks, and heartbeats to see the truth. For Redis specifically, see Redis and ElastiCache monitoring.

Monitoring the queue and scheduler

Background processing is where Laravel monitoring most often falls short. Your site can be flawlessly up while the scheduler has stopped firing or every queue worker has crashed — and no HTTP check will ever notice, because nobody is making a request that fails. You find out from a customer days later.

The fix is heartbeat monitoring, sometimes called dead-man's-switch monitoring. Instead of CronAlert reaching in to your worker, your job reaches out: every time the scheduler runs or a worker completes a run of critical work, it makes a quick HTTP request to a unique CronAlert heartbeat URL. You tell CronAlert how often to expect that ping plus a grace period, and if a ping fails to arrive on schedule, CronAlert alerts you that the job went silent. The contract is inverted from an uptime check — silence is the alarm.

For the scheduler, add a lightweight scheduled task that pings the heartbeat on every run; if the ping stops, you know php artisan schedule:run is no longer firing, which is the single most common silent Laravel failure. For queues, have a periodic job — or your most critical recurring job — ping a second heartbeat after it confirms the worker can reach the broker and database, so a dead worker pool surfaces as a missed ping. The Laravel ecosystem has spatie/laravel-schedule-monitor for tracking scheduled tasks specifically, but a CronAlert heartbeat covers both the scheduler and workers provider-agnostically, with the same alert channels as the rest of your monitoring. Our guides to cron job heartbeat monitoring and background worker monitoring walk through the patterns in depth, and for one-shot scheduled jobs see batch job monitoring. Heartbeat checks are available on every CronAlert plan, including the free tier.

Catching "up but wrong" with content checks

A Laravel response can be a perfect HTTP 200 and still be completely broken. A deploy that shipped an empty Blade view, a page rendering a styled "something went wrong" screen, or a homepage missing its content because the Vite build 404ed will all pass a status-code check. To catch these you have to assert on what the page actually contains.

The simplest tool is a keyword assertion: pick a string that appears only on a correctly rendered page — a navigation label, a product name, your footer copyright line — and require the check to find it. If a broken deploy strips that content or replaces the page with an error screen, the keyword disappears and the check fails even though the status code is 200. You can also assert that an error-page string is absent, so a leaked Whoops debug page (which contains telltale phrases) trips the alarm — a fast way to catch APP_DEBUG=true in production. Our keyword monitoring guide covers building robust assertions, including regex matching on Pro.

For pages that are supposed to be byte-for-byte stable — a marketing page, a terms-of-service document, a generated status snapshot — a SHA-256 content hash is even stronger. CronAlert hashes the rendered output and alerts you the instant it changes, which catches both an unexpected drift and a defacement. See content monitoring for how content-hash checks work in practice.

SSL and response-time thresholds

Two more checks round out a Laravel setup. First, certificate monitoring: an expired TLS certificate breaks the site for every browser no matter how healthy the app is, and certificate renewals fail more often than people expect. Point an SSL check at your domain and get warned days before expiry — see SSL certificate monitoring.

Second, response-time thresholds. Laravel routes that creep slower — an N+1 query, an unindexed lookup, a degraded cache, or worker contention under Octane — are an early warning of trouble well before they turn into worker exhaustion and 502s. Configure a response-time threshold on your most important monitors so a route that crosses, say, two seconds alerts you while it is still merely slow rather than down. Our guide to timeout thresholds explains how to pick sensible numbers without generating noise.

A concrete CronAlert setup

Here is an end-to-end setup you can replicate in a few minutes after you create a free CronAlert account:

  • Expose a dependency-aware health check. Use the built-in /up route and hook the DiagnosingHealth event to verify the database, cache, and queue — or install spatie/laravel-health for batteries included.
  • Create an HTTP monitor for the health route. Point a CronAlert HTTP monitor at https://yourapp.com/up, expect a 200, and set the interval (3 minutes free, 1 minute on Pro).
  • Add a keyword assertion to the homepage. Create a second monitor on your homepage and require a string only present when the page renders correctly, so a broken deploy fails the check.
  • Add API and login monitors. Check a key API route and your login page so your control plane and integrators are covered.
  • Add a heartbeat for the scheduler. Create a heartbeat monitor, set the expected interval and grace period, and add a scheduled task that pings its URL on every run.
  • Add a heartbeat for queue workers. Create a second heartbeat and have a critical recurring job ping it after confirming it can reach the broker and database.
  • Turn on SSL and response-time checks. Enable certificate monitoring on your domain and set a response-time threshold on your critical monitors.
  • Wire up alert channels. Connect email, Slack, Discord, Teams, Telegram, PagerDuty, Opsgenie, Splunk On-Call, a webhook, or PWA push — so the right people hear about it the right way.

Every plan includes the full REST API, and CronAlert ships an MCP server so you can manage monitors directly from Claude Code, Cursor, or Windsurf. Teams that need multi-region coverage can move to the Team plan, which checks from five edge regions with quorum so a single regional blip never pages you.

Frequently asked questions

Does Laravel have a built-in health check endpoint?

Yes. Laravel 11 and newer ship a built-in health route at /up out of the box, defined in bootstrap/app.php. It boots the framework, dispatches a DiagnosingHealth event, and returns HTTP 200 when the application is healthy. On older Laravel versions you can add a simple route yourself or install spatie/laravel-health, which provides configurable checks for the database, cache, queue, disk space, and more.

Should the Laravel health endpoint check the database and cache?

For a meaningful signal, yes. The default /up route confirms the framework boots but does not verify your dependencies, so a deep check should run a trivial query, do a quick cache round-trip, and confirm the queue connection is reachable. You can hook into the DiagnosingHealth event to add those assertions, or use spatie/laravel-health. Keep every check fast so the endpoint never becomes a bottleneck, and return a non-200 when any critical dependency is down.

How do I monitor Laravel queue workers and the scheduler?

Use heartbeat monitoring. Have a scheduled Artisan command or a queued job make an HTTP request to a CronAlert heartbeat URL every time it runs successfully, then set the heartbeat's expected interval and grace period. If the scheduler stops firing or the worker pool dies, the ping stops arriving and CronAlert alerts you that the job went silent. This catches the classic "did my Laravel scheduler actually run?" problem that HTTP checks miss entirely.

Why does my Laravel site return 503 to a monitor?

A 503 from Laravel almost always means the app is in maintenance mode. Running php artisan down puts the app behind a maintenance page and returns HTTP 503 for every request, and it is easy to leave enabled after a deploy that failed partway through. If a monitor starts reporting 503s right after a deploy, check whether php artisan up was ever run. You can allow a monitor through with a secret bypass, but treating a stuck maintenance mode as an outage is usually the right call.

Can uptime monitoring detect a broken Laravel deploy that still returns 200?

Yes, with keyword and content checks. A deploy that skipped migrations, left APP_DEBUG=true, or shipped an empty Blade view can still return 200, so a status-code-only check passes while users see a broken or leaking page. Configure a keyword assertion that requires a string only present on a correctly rendered page, and optionally assert that a Whoops error-page phrase is absent. For pages that should never change, a SHA-256 content hash alerts you the moment the rendered output drifts.

Start monitoring your Laravel app

Laravel apps fail in ways a single homepage ping will never catch: dead queue workers, a scheduler that stopped firing, a stuck maintenance mode, unrun migrations, and broken deploys that still answer 200. The built-in /up route made dependency-aware, content assertions, heartbeats for the scheduler and queues, and SSL plus response-time checks together give you a signal you can trust. CronAlert covers all of it agentlessly from the edge, and the free plan is enough to wire up your first complete setup today. Create a free CronAlert account and point your first check at /up in the next five minutes.

Related reading: Cron job heartbeat monitoring, Background worker monitoring, Building a database health endpoint, and Uptime monitoring for Django applications.