Laravel

How to Monitor Laravel Cron Jobs with Cronaman

April 27, 2026·6 min read

The silent failure problem

Laravel's Task Scheduling is one of the framework's most convenient features. You define all your scheduled tasks in routes/console.php (or the older Kernel.php), add a single crontab entry, and Laravel handles the rest.

The downside: when a scheduled command fails — an exception, a timeout, a misconfigured environment — it logs the error and moves on. There's no built-in mechanism to alert you. Backup commands, invoice generators, and cleanup jobs fail silently for days before anyone notices.

What is heartbeat monitoring?

Heartbeat monitoring gives you an external watchdog. You configure an expected interval in Cronaman, and your command sends a simple HTTP ping after each successful execution. If the ping doesn't arrive within the window, Cronaman alerts you — no polling, no log scraping.

Laravel makes this especially clean: the scheduler's onSuccess and onFailure hooks let you attach the ping directly to any scheduled task without modifying the command itself.

Create a Cronaman monitor

Before touching any code, create a monitor in Cronaman:

  • Sign up at cronaman.dev — free plan, no credit card required
  • Click New Monitor
  • Name it (e.g., "Invoice generator") and set the interval to match your schedule
  • Copy your unique ping URL: https://cronaman.dev/ping/invoice-generator

Using onSuccess and onFailure hooks

Laravel 10+ exposes onSuccess() and onFailure() on every scheduled task. Add your Cronaman pings here — no changes to the command class needed:

routes/console.php
use IlluminateSupportFacadesHttp;
use IlluminateSupportFacadesSchedule;

Schedule::command('invoices:generate')
    ->daily()
    ->onSuccess(function () {
        Http::timeout(10)->get('https://cronaman.dev/ping/invoice-generator');
    })
    ->onFailure(function () {
        Http::timeout(10)->get('https://cronaman.dev/ping/invoice-generator/fail');
    });

Laravel's Http facade (backed by Guzzle) handles the HTTP call. Always set timeout() — Guzzle has no default timeout and will block indefinitely without one.

The older Kernel.php approach

On Laravel 9 or older projects still using app/Console/Kernel.php, the hooks work identically:

app/Console/Kernel.php
use IlluminateSupportFacadesHttp;

protected function schedule(Schedule $schedule): void
{
    $schedule->command('invoices:generate')
        ->daily()
        ->onSuccess(function () {
            Http::timeout(10)->get('https://cronaman.dev/ping/invoice-generator');
        })
        ->onFailure(function () {
            Http::timeout(10)->get('https://cronaman.dev/ping/invoice-generator/fail');
        });
}

Pinging from inside a command

If you prefer to keep the monitoring logic co-located with the command itself, add the ping inside the handle() method:

app/Console/Commands/GenerateInvoices.php
<?php

namespace AppConsoleCommands;

use IlluminateConsoleCommand;
use IlluminateSupportFacadesHttp;

class GenerateInvoices extends Command
{
    const PING_URL = 'https://cronaman.dev/ping/invoice-generator';
    const FAIL_URL = 'https://cronaman.dev/ping/invoice-generator/fail';

    protected $signature   = 'invoices:generate';
    protected $description = 'Generate monthly invoices';

    public function handle(): int
    {
        try {
            // Your job logic here
            $this->generateInvoices();

            Http::timeout(10)->get(self::PING_URL);  // success
            return Command::SUCCESS;
        } catch (Throwable $e) {
            $this->error("Failed: {$e->getMessage()}");
            Http::timeout(10)->get(self::FAIL_URL);  // failure
            return Command::FAILURE;
        }
    }
}

The single crontab entry

Laravel Task Scheduling only requires one entry in your server's crontab — the scheduler itself decides which tasks run and when:

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

Set your Cronaman monitor interval to match your command's schedule (e.g., 24 hours for a daily task) and add a 10–15 minute grace period to absorb scheduler startup time and minor clock drift.

Verify your setup

Run the command manually to confirm the ping fires:

terminal
php artisan invoices:generate

Open your Cronaman dashboard. Within a few seconds the monitor should show "healthy" with a "Last ping" timestamp. If it stays grey, confirm outbound HTTPS from your server:

terminal
curl -v https://cronaman.dev/ping/invoice-generator

More cron monitoring guides

Using a different stack? The same heartbeat pattern works everywhere:

Start monitoring your Laravel cron jobs

Free forever for up to 3 monitors. No credit card required. Set up in under 2 minutes.

Start monitoring free