PHPackages                             spatie/laravel-schedule-monitor - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. spatie/laravel-schedule-monitor

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

spatie/laravel-schedule-monitor
===============================

Monitor scheduled tasks in a Laravel app

4.3.0(1mo ago)9815.7M↑16.6%738MITPHPPHP ^8.2CI passing

Since Jul 8Pushed 1mo ago6 watchersCompare

[ Source](https://github.com/spatie/laravel-schedule-monitor)[ Packagist](https://packagist.org/packages/spatie/laravel-schedule-monitor)[ Docs](https://github.com/spatie/laravel-schedule-monitor)[ GitHub Sponsors](https://github.com/spatie)[ RSS](/packages/spatie-laravel-schedule-monitor/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (24)Versions (63)Used By (8)

Monitor scheduled tasks in a Laravel app
========================================

[](#monitor-scheduled-tasks-in-a-laravel-app)

[![Latest Version on Packagist](https://camo.githubusercontent.com/651b990716b41a6d2eaf09fe4d60fc639109b0b094d867342fe23628dd3d3d5e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7370617469652f6c61726176656c2d7363686564756c652d6d6f6e69746f722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/spatie/laravel-schedule-monitor)[![Total Downloads](https://camo.githubusercontent.com/cad36838ab61a4d8c374872bdde3d265396df119656e232d0eb413b14d2c8bb8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7370617469652f6c61726176656c2d7363686564756c652d6d6f6e69746f722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/spatie/laravel-schedule-monitor)

This package will monitor your Laravel schedule. It will write an entry to a log table in the db each time a schedule tasks starts, end, fails or is skipped. Using the `list` command you can check when the scheduled tasks have been executed.

[![screenshot](https://github.com/spatie/laravel-schedule-monitor/raw/main/docs/list-with-failure.png)](https://github.com/spatie/laravel-schedule-monitor/blob/main/docs/list-with-failure.png)

This package can also sync your schedule with [Oh Dear](https://ohdear.app). Oh Dear will send you a notification whenever a scheduled task doesn't run on time or fails.

Support us
----------

[](#support-us)

[![](https://camo.githubusercontent.com/1e5c166dabf773658a2322a1a5e559d3d25c12b27b8e1e86878cd5427ff5c2ba/68747470733a2f2f6769746875622d6164732e73332e65752d63656e7472616c2d312e616d617a6f6e6177732e636f6d2f6c61726176656c2d7363686564756c652d6d6f6e69746f722e6a70673f743d31)](https://spatie.be/github-ad-click/laravel-schedule-monitor)

We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require spatie/laravel-schedule-monitor
```

If you need Laravel 8 support, you can install v2 of the package using `composer require spatie/laravel-schedule-monitor:^2`.

#### Preparing the database

[](#preparing-the-database)

You must publish and run migrations:

```
php artisan vendor:publish --provider="Spatie\ScheduleMonitor\ScheduleMonitorServiceProvider" --tag="schedule-monitor-migrations"
php artisan migrate
```

#### Publishing the config file

[](#publishing-the-config-file)

You can publish the config file with:

```
php artisan vendor:publish --provider="Spatie\ScheduleMonitor\ScheduleMonitorServiceProvider" --tag="schedule-monitor-config"
```

This is the contents of the published config file:

```
return [
    /*
     * The schedule monitor will log each start, finish and failure of all scheduled jobs.
     * After a while the `monitored_scheduled_task_log_items` might become big.
     * Here you can specify the amount of days log items should be kept.
     *
     * Use Laravel's pruning command to delete old `MonitoredScheduledTaskLogItem` models.
     * More info: https://laravel.com/docs/11.x/eloquent#pruning-models
     */
    'delete_log_items_older_than_days' => 30,

    /*
     * The date format used for all dates displayed on the output of commands
     * provided by this package.
     */
    'date_format' => 'Y-m-d H:i:s',

    'models' => [
        /*
         * The model you want to use as a MonitoredScheduledTask model needs to extend the
         * `Spatie\ScheduleMonitor\Models\MonitoredScheduledTask` Model.
         */
        'monitored_scheduled_task' => Spatie\ScheduleMonitor\Models\MonitoredScheduledTask::class,

        /*
         * The model you want to use as a MonitoredScheduledTaskLogItem model needs to extend the
         * `Spatie\ScheduleMonitor\Models\MonitoredScheduledTaskLogItem` Model.
         */
        'monitored_scheduled_log_item' => Spatie\ScheduleMonitor\Models\MonitoredScheduledTaskLogItem::class,
    ],

    /*
     * Oh Dear can notify you via Mail, Slack, SMS, web hooks, ... when a
     * scheduled task does not run on time.
     *
     * More info: https://ohdear.app/docs/features/cron-job-monitoring
     */
    'oh_dear' => [
        /*
         * You can generate an API token at the Oh Dear user settings screen
         *
         * https://ohdear.app/user/api-tokens
         */
        'api_token' => env('OH_DEAR_API_TOKEN', ''),

        /*
         *  The id of the monitor you want to sync the schedule with.
         *
         * You'll find this id on the settings page of a monitor at Oh Dear.
         */
        'monitor_id' => env('OH_DEAR_MONITOR_ID'),

        /*
         * To keep scheduled jobs as short as possible, Oh Dear will be pinged
         * via a queued job. Here you can specify the name of the queue you wish to use.
         */
        'queue' => env('OH_DEAR_QUEUE'),

        /*
         * The job class that will be dispatched to ping Oh Dear.
         */
        'ping_oh_dear_job' => Spatie\ScheduleMonitor\Jobs\PingOhDearJob::class,

        /*
         * `PingOhDearJob`s will automatically be skipped if they've been queued for
         * longer than the time configured here.
         */
        'retry_job_for_minutes' => 10,

        /*
         * When set to true, we will automatically add the `PingOhDearJob` to Horizon's
         * silenced jobs.
         */
        'silence_ping_oh_dear_job_in_horizon' => true,

        /*
         * Send the start of a scheduled job to Oh Dear. This is not needed
         * for notifications to work correctly.
         */
        'send_starting_ping' => env('OH_DEAR_SEND_STARTING_PING', false),

        /**
         * The amount of minutes a scheduled task is allowed to run before it is
         * considered late.
         */
        'grace_time_in_minutes' => 5,

        /**
         * Which endpoint to ping on Oh Dear.
         */
        'endpoint_url' => env('OH_DEAR_PING_ENDPOINT_URL'),

        /**
         * The URL of the Oh Dear API.
         */
        'api_url' => env('OH_DEAR_API_URL', 'https://ohdear.app/api/'),

        /*
         * The delay in milliseconds between retry attempts when a ping fails.
         */
        'retry_delay_ms' => env('OH_DEAR_RETRY_DELAY_MS', 10_000),

        /*
         * When enabled, failed pings to Oh Dear will log detailed diagnostics
         * including request timing, connection info, and response data.
         * Useful for debugging connectivity issues with ping.ohdear.app.
         */
        'debug_logging' => env('OH_DEAR_DEBUG_LOGGING', false),
    ],
];
```

#### Debugging failed pings

[](#debugging-failed-pings)

If you're experiencing intermittent timeouts or connection issues when pinging Oh Dear, you can enable debug logging to get detailed diagnostics on every failed ping:

```
OH_DEAR_DEBUG_LOGGING=true
```

When enabled, each failed ping will write a `Log::warning` with the full request details, cURL timing breakdown (DNS lookup, TCP connect, TLS handshake, time-to-first-byte), connection info (resolved IP, local IP), and the server response if one was received.

#### Cleaning the database

[](#cleaning-the-database)

The schedule monitor will log each start, finish and failure of all scheduled jobs. After a while the `monitored_scheduled_task_log_items` might become big.

Use [Laravel's model pruning feature](https://laravel.com/docs/9.x/eloquent#pruning-models) , you can delete old `MonitoredScheduledTaskLogItem` models. Models older than the amount of days configured in the `delete_log_items_older_than_days` in the `schedule-monitor` config file, will be deleted.

```
// app/Console/Kernel.php

use Spatie\ScheduleMonitor\Models\MonitoredScheduledTaskLogItem;

class Kernel extends ConsoleKernel
{
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('model:prune', ['--model' => MonitoredScheduledTaskLogItem::class])->daily();
    }
}
```

#### Syncing the schedule

[](#syncing-the-schedule)

Every time you deploy your application, you should execute the `schedule-monitor:sync` command

```
php artisan schedule-monitor:sync
```

This command is responsible for syncing your schedule with the database, and optionally Oh Dear. We highly recommend adding this command to the script that deploys your production environment.

In a non-production environment you should manually run `schedule-monitor:sync`. You can verify if everything synced correctly using `schedule-monitor:list`.

**Note:** Running the sync command will remove any other cron monitors that you've defined other than the application schedule.

If you would like to use non-destructive syncs to Oh Dear so that you can monitor other cron tasks outside of Laravel, you can use the `--keep-old` flag. This will only push new tasks to Oh Dear, rather than a full sync. Note that this will not remove any tasks from Oh Dear that are no longer in your schedule.

Usage
-----

[](#usage)

To monitor your schedule you should first run `schedule-monitor:sync`. This command will take a look at your schedule and create an entry for each task in the `monitored_scheduled_tasks` table.

[![screenshot](https://github.com/spatie/laravel-schedule-monitor/raw/main/docs/sync.png)](https://github.com/spatie/laravel-schedule-monitor/blob/main/docs/sync.png)

To view all monitored scheduled tasks, you can run `schedule-monitor:list`. This command will list all monitored scheduled tasks. It will show you when a scheduled task has last started, finished, or failed.

[![screenshot](https://github.com/spatie/laravel-schedule-monitor/raw/main/docs/list.png)](https://github.com/spatie/laravel-schedule-monitor/blob/main/docs/list.png)

The package will write an entry to the `monitored_scheduled_task_log_items` table in the db each time a schedule tasks starts, end, fails or is skipped. Take a look at the contents of that table if you want to know when and how scheduled tasks did execute. The log items also hold other interesting metrics like memory usage, execution time, and more.

### Naming tasks

[](#naming-tasks)

Schedule monitor will try to automatically determine a name for a scheduled task. For commands this is the command name, for anonymous jobs the class name of the first argument will be used. For some tasks, like scheduled closures, a name cannot be determined automatically.

To manually set a name of the scheduled task, you can tack on `monitorName()`.

Here's an example.

```
// in app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
   $schedule->command('your-command')->daily()->monitorName('a-custom-name');
   $schedule->call(fn () => 1 + 1)->hourly()->monitorName('addition-closure');
}
```

When you change the name of task, the schedule monitor will remove all log items of the monitor with the old name, and create a new monitor using the new name of the task.

### Setting a grace time

[](#setting-a-grace-time)

When the package detects that the last run of a scheduled task did not run in time, the `schedule-monitor` list will display that task using a red background color. In this screenshot the task named `your-command` ran too late.

[![screenshot](https://github.com/spatie/laravel-schedule-monitor/raw/main/docs/list-with-failure.png)](https://github.com/spatie/laravel-schedule-monitor/blob/main/docs/list-with-failure.png)

The package will determine that a task ran too late if it was not finished at the time it was supposed to run + the grace time. You can think of the grace time as the number of minutes that a task under normal circumstances needs to finish. By default, the package grants a grace time of 5 minutes to each task.

You can customize the grace time by using the `graceTimeInMinutes` method on a task. In this example a grace time of 10 minutes is used for the `your-command` task.

```
// in app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
   $schedule->command('your-command')->daily()->graceTimeInMinutes(10);
}
```

### Ignoring scheduled tasks

[](#ignoring-scheduled-tasks)

You can avoid a scheduled task being monitored by tacking on `doNotMonitor` when scheduling the task.

```
// in app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
   $schedule->command('your-command')->daily()->doNotMonitor();
}
```

### Storing output in the database

[](#storing-output-in-the-database)

You can store the output by tacking on `storeOutputInDb` when scheduling the task.

```
// in app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
   $schedule->command('your-command')->daily()->storeOutputInDb();
}
```

The output will be stored in the `monitored_scheduled_task_log_items` table, in the `output` key of the `meta` column.

### Multitenancy

[](#multitenancy)

If you're using [spatie/laravel-multitenancy](https://github.com/spatie/laravel-multitenancy) you should add the `PingOhDearJob` to the `not_tenant_aware_jobs` array in `config/multitenancy.php`.

```
'not_tenant_aware_jobs' => [
    // ...
    \Spatie\ScheduleMonitor\Jobs\PingOhDearJob::class,
]
```

Without it, the `PingOhDearJob` will fail as no tenant will be set.

### Getting notified when a scheduled task doesn't finish in time

[](#getting-notified-when-a-scheduled-task-doesnt-finish-in-time)

This package can sync your schedule with the [Oh Dear](https://ohdear.app) cron check. Oh Dear will send you a notification whenever a scheduled task does not finish on time.

You eed to make sure the `api_token` and `monitor_id` keys of the `schedule-monitor` are filled with an API token, and an Oh Dear monitor id. To verify that these values hold correct values you can run this command.

```
php artisan schedule-monitor:verify
```

[![screenshot](https://github.com/spatie/laravel-schedule-monitor/raw/main/docs/verify.png)](https://github.com/spatie/laravel-schedule-monitor/blob/main/docs/verify.png)

To sync your schedule with Oh Dear run this command:

```
php artisan schedule-monitor:sync
```

[![screenshot](https://github.com/spatie/laravel-schedule-monitor/raw/main/docs/sync-oh-dear.png)](https://github.com/spatie/laravel-schedule-monitor/blob/main/docs/sync-oh-dear.png)

After that, the `list` command should show that all the scheduled tasks in your app are registered on Oh Dear.

[![screenshot](https://github.com/spatie/laravel-schedule-monitor/raw/main/docs/list-oh-dear.png)](https://github.com/spatie/laravel-schedule-monitor/blob/main/docs/list-oh-dear.png)

To keep scheduled jobs as short as possible, Oh Dear will be pinged via queued jobs. To ensure speedy delivery to Oh Dear, and to avoid false positive notifications, we highly recommend creating a dedicated queue for these jobs. You can put the name of that queue in the `queue` key of the config file.

Oh Dear will wait for the completion of a schedule tasks for a given amount of minutes. This is called the grace time. By default, all scheduled tasks will have a grace time of 5 minutes. To customize this value, you can tack on `graceTimeInMinutes` to your scheduled tasks.

Here's an example where Oh Dear will send a notification if the task didn't finish by 00:10.

```
// in app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
   $schedule->command('your-command')->daily()->graceTimeInMinutes(10);
}
```

### Disabling Oh Dear for individual tasks

[](#disabling-oh-dear-for-individual-tasks)

If you want to have a task monitored by the schedule monitor, but not by Oh Dear, you can tack on `doMonitorAtOhDear` to your scheduled tasks.

```
// in app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
   $schedule->command('your-command')->daily()->doNotMonitorAtOhDear();
}
```

### Customizing the Oh Dear ping job

[](#customizing-the-oh-dear-ping-job)

By default, the package dispatches `Spatie\ScheduleMonitor\Jobs\PingOhDearJob` to send pings to Oh Dear. You can customize this job class.

To use a custom job class, update the `ping_oh_dear_job` configuration in your `config/schedule-monitor.php` file:

```
'oh_dear' => [
    // ...
    'ping_oh_dear_job' => \App\Jobs\CustomPingOhDearJob::class,
],
```

Do make sure your custom job class extends `Spatie\ScheduleMonitor\Jobs\PingOhDearJob` to retain the original functionality. Here's an example of a custom job class:

```
namespace App\Jobs;

use Spatie\ScheduleMonitor\Jobs\PingOhDearJob;
use Spatie\ScheduleMonitor\Models\MonitoredScheduledTaskLogItem;

class CustomPingOhDearJob extends PingOhDearJob
{
    // your implementation
}
```

Unsupported methods
-------------------

[](#unsupported-methods)

Currently, this package does not work for tasks that use these methods:

- `between`
- `unlessBetween`
- `when`
- `skip`

Third party scheduled task monitors
-----------------------------------

[](#third-party-scheduled-task-monitors)

We assume that, when your scheduled tasks do not run properly, a scheduled task that sends out notifications would probably not run either. That's why this package doesn't send out notifications by itself.

These services can notify you when scheduled tasks do not run properly:

- [Oh Dear](https://ohdear.app)
- [thenping.me](https://thenping.me)
- [Healthchecks.io](https://healthchecks.io)
- [Cronitor](https://cronitor.io)
- [Cronhub](https://cronhub.io/)
- [DeadMansSnitch](https://deadmanssnitch.com/)
- [CronAlarm](https://www.cronalarm.com/)
- [PushMon](https://www.pushmon.com/)

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you've found a bug regarding security please mail  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Freek Van der Herten](https://github.com/freekmurze)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

73

—

ExcellentBetter than 100% of packages

Maintenance89

Actively maintained with recent releases

Popularity68

Solid adoption and visibility

Community37

Small or concentrated contributor base

Maturity81

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 65.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~35 days

Recently: every ~52 days

Total

60

Last Release

55d ago

Major Versions

0.0.4 → 1.0.02020-07-08

1.0.4 → 2.0.02020-09-29

2.4.7 → 3.0.02022-01-14

2.4.8 → 3.10.12025-02-21

3.10.3 → 4.0.02025-08-26

PHP version history (5 changes)0.0.1PHP ^7.4

2.1.0PHP ^7.4|^8.0

3.0.0PHP ^8.0

3.9.0PHP ^8.1

4.3.0PHP ^8.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/7535935?v=4)[Spatie](/maintainers/spatie)[@spatie](https://github.com/spatie)

---

Top Contributors

[![freekmurze](https://avatars.githubusercontent.com/u/483853?v=4)](https://github.com/freekmurze "freekmurze (245 commits)")[![laravel-shift](https://avatars.githubusercontent.com/u/15991828?v=4)](https://github.com/laravel-shift "laravel-shift (13 commits)")[![frankperez87](https://avatars.githubusercontent.com/u/467730?v=4)](https://github.com/frankperez87 "frankperez87 (12 commits)")[![mattiasgeniar](https://avatars.githubusercontent.com/u/407270?v=4)](https://github.com/mattiasgeniar "mattiasgeniar (12 commits)")[![cretueusebiu](https://avatars.githubusercontent.com/u/1517945?v=4)](https://github.com/cretueusebiu "cretueusebiu (10 commits)")[![m-bymike](https://avatars.githubusercontent.com/u/4008663?v=4)](https://github.com/m-bymike "m-bymike (8 commits)")[![keithbrink](https://avatars.githubusercontent.com/u/15267205?v=4)](https://github.com/keithbrink "keithbrink (8 commits)")[![Nielsvanpach](https://avatars.githubusercontent.com/u/10651054?v=4)](https://github.com/Nielsvanpach "Nielsvanpach (8 commits)")[![AdrianMrn](https://avatars.githubusercontent.com/u/12762044?v=4)](https://github.com/AdrianMrn "AdrianMrn (7 commits)")[![xiCO2k](https://avatars.githubusercontent.com/u/823088?v=4)](https://github.com/xiCO2k "xiCO2k (6 commits)")[![resohead](https://avatars.githubusercontent.com/u/13508056?v=4)](https://github.com/resohead "resohead (6 commits)")[![AlexVanderbist](https://avatars.githubusercontent.com/u/6287961?v=4)](https://github.com/AlexVanderbist "AlexVanderbist (5 commits)")[![yoeriboven](https://avatars.githubusercontent.com/u/4047804?v=4)](https://github.com/yoeriboven "yoeriboven (4 commits)")[![smknstd](https://avatars.githubusercontent.com/u/2412608?v=4)](https://github.com/smknstd "smknstd (4 commits)")[![patrickbrouwers](https://avatars.githubusercontent.com/u/7728097?v=4)](https://github.com/patrickbrouwers "patrickbrouwers (4 commits)")[![patinthehat](https://avatars.githubusercontent.com/u/5508707?v=4)](https://github.com/patinthehat "patinthehat (3 commits)")[![mennorenkens](https://avatars.githubusercontent.com/u/18008421?v=4)](https://github.com/mennorenkens "mennorenkens (2 commits)")[![bastien-phi](https://avatars.githubusercontent.com/u/10199039?v=4)](https://github.com/bastien-phi "bastien-phi (2 commits)")[![oddvalue](https://avatars.githubusercontent.com/u/10127404?v=4)](https://github.com/oddvalue "oddvalue (2 commits)")[![bilfeldt](https://avatars.githubusercontent.com/u/30228807?v=4)](https://github.com/bilfeldt "bilfeldt (2 commits)")

---

Tags

laravelmonitorphpschedulespatielaravel-schedule-monitor

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/spatie-laravel-schedule-monitor/health.svg)

```
[![Health](https://phpackages.com/badges/spatie-laravel-schedule-monitor/health.svg)](https://phpackages.com/packages/spatie-laravel-schedule-monitor)
```

###  Alternatives

[spatie/laravel-data

Create unified resources and data transfer objects

1.8k28.9M627](/packages/spatie-laravel-data)[spatie/laravel-analytics

A Laravel package to retrieve Google Analytics data.

3.2k5.7M57](/packages/spatie-laravel-analytics)[spatie/laravel-sitemap

Create and generate sitemaps with ease

2.6k14.6M107](/packages/spatie-laravel-sitemap)[spatie/laravel-responsecache

Speed up a Laravel application by caching the entire response

2.8k8.2M51](/packages/spatie-laravel-responsecache)[spatie/laravel-honeypot

Preventing spam submitted through forms

1.6k6.0M60](/packages/spatie-laravel-honeypot)[spatie/laravel-health

Monitor the health of a Laravel application

86910.0M83](/packages/spatie-laravel-health)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
