Laravel scheduler monitoring

Add a 10-second heartbeat task that posts to an external monitor only while your job last succeeded within a set window, so one alert covers both a failed task and a scheduler that is not running at all.

The Laravel scheduler fails in two ways. A task exits non-zero, or nothing runs at all: the cron line for schedule:run never made it to the new server, the container running schedule:work was replaced, or someone ran php artisan down and every task skipped. The second is worse: nothing logs a process that never started.

Laravel cron monitoring with pingOnSuccess

Laravel has pings built in. pingBefore, thenPing, pingOnSuccess and pingOnFailure call a URL around a task, and onSuccess only fires on exit code 0. There are two catches with Logdash. The scheduler sends those pings as GET, and the Logdash ping route only accepts POST, so they get a 404. And Logdash push monitors, a Pro feature at $15 a month, are checked every 15 seconds: no ping in that window and the monitor goes down. A daily task cannot keep that up by pinging after itself.

Laravel scheduler monitoring with a heartbeat

So split it. The real task writes its last success to the cache. A sub-minute task, which the scheduler supports natively, posts to Logdash every 10 seconds while that success is younger than your window.

routes/console.php
<?php

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Schedule;

// The task you care about. onSuccess only fires on exit code 0.
Schedule::command('backup:run')
    ->dailyAt('03:00')
    ->runInBackground()
    ->onSuccess(fn () => Cache::forever('backup:ok', now()->timestamp));

// The heartbeat. Stops when the scheduler stops or the backup goes stale.
Schedule::call(function () {
    if (now()->timestamp - Cache::get('backup:ok', 0) < 25 * 3600) {
        Http::timeout(5)->post('https://api.logdash.io/ping/68b4c1f0e3a2d5c7b9f01234');
    }
})->name('logdash-heartbeat')->everyTenSeconds();
  • schedule:run not running: no heartbeat, alert within 30 seconds. A missing crontab line, a dead schedule:work container, a server stuck after a reboot.
  • The backup exits non-zero or hangs: onSuccess never fires, the timestamp ages, and the pings stop 25 hours after the last good run.
  • Maintenance mode: scheduled tasks skip, the heartbeat included, so a forgotten php artisan down reaches you too.

Two details matter. Keep runInBackground on long tasks, as the Laravel docs advise for schedules with sub-minute tasks, or a slow backup can delay the heartbeat and trip a false alert. And use a shared, persistent cache store, because the array driver forgets the timestamp between runs. After each deploy, run php artisan schedule:interrupt so the running scheduler picks up the new code.

  1. Create a push monitor Add a service in Logdash on Pro, set the monitor to push and copy the monitor id from the ping URL. Name it after the task, since the alert shows the name.
  2. Add the two tasks Paste them into routes/console.php with your window: the task interval plus the lateness you accept. The monitor reads down until the first backup succeeds, which is the truth.
  3. Stop the scheduler Comment out the schedule:run cron line or stop the schedule:work process. Within 30 seconds a Telegram message lands saying the monitor is down, status code 0, did not receive call for this time range.

Laravel schedule monitor packages

spatie/laravel-schedule-monitor logs every start, finish and failure of every task to two tables, lists them with schedule-monitor:list, and flags a task as late when it misses its time plus a grace period, 5 minutes by default. It does not send alerts, on purpose: if the scheduler is broken, a scheduled notification would not run either. Alerts come from syncing the schedule to Oh Dear, which starts at 15 euros a month and has no free plan.

Logdash vs spatie/laravel-schedule-monitor

FeatureLogdashSpatie package with Oh Dear
Tasks coveredThe ones you wire up, one cache key eachEvery task after one sync command
Late and failed runsOne freshness window per taskSchedule-aware, grace time per task
Run historyUp and down history per monitorStart, finish, runtime and memory in your database
Scheduler not runningAlert within 30 seconds, without Oh DearOnly through Oh Dear
Alert channelsTelegram and webhookMail, Slack, SMS, webhooks and more
PricePro, $15 a monthPackage free, Oh Dear from 15 euros a month

When spatie/laravel-schedule-monitor is the better pick

  • You have more than a handful of tasks. One sync and every task is monitored, with no cache keys to keep in step.
  • You want runtime and memory per run in your own database, not only up or down.
  • You already pay for Oh Dear. The scheduled task check comes with every plan.
  • Your tasks run weekdays only or on odd schedules, where one fixed window either misses late runs or alerts every weekend.
What is the simplest Laravel scheduler monitoring setup?
A sub-minute heartbeat task that posts to an external monitor while your important task last succeeded within a window. It catches failed tasks and a dead scheduler with about ten lines in routes/console.php.
Is there a Laravel schedule monitor package?
spatie/laravel-schedule-monitor records every run and flags late tasks in schedule-monitor:list. It sends no alerts itself; for those it syncs your schedule to Oh Dear, a paid service.
How do I tell if the Laravel scheduler is not running?
Something outside the app has to notice the silence. Check that the cron entry for schedule:run exists on the server that is actually live, then add a heartbeat so the next time it stops you hear about it within 30 seconds instead of next week.
Can I use pingOnSuccess for Laravel cron monitoring with Logdash?
Not directly. pingOnSuccess sends a GET and the Logdash ping route only takes POST. One ping after a daily task also cannot satisfy a 15-second check window, so use the heartbeat pattern above.

Point it at your own URL and watch it for real.