← All posts

Laravel's scheduler: one cron line runs everything

Instead of a messy crontab with a line per task, Laravel lets you define every scheduled job in code and run them all from a single cron entry.

Written byMuwafaq Murad

Laravel's scheduler: one cron line runs everything, cover

Most apps need things to happen on a timer: send a daily report, clean old records, sync data from another system every hour, remind customers about appointments. The old way was one crontab line per task on the server. Nobody remembered they were there, and they were not in version control.

Laravel's scheduler fixes that.

The only cron line you need

On the server, add one entry to the crontab:

* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1

Every minute, Laravel checks which tasks are due and runs them. Everything else lives in your code.

Defining tasks

In recent Laravel versions, tasks go in routes/console.php:

use Illuminate\Support\Facades\Schedule;

Schedule::command('reports:daily')->dailyAt('07:00');
Schedule::command('orders:sync')->hourly();
Schedule::job(new PruneOldUploads)->weekly();

Now the schedule is in Git, reviewed like any other code, and the same on every server.

Options I use a lot

  • withoutOverlapping() stops a task from starting again while the last run is still going. Important for syncs that sometimes take longer than expected.

  • onOneServer() makes sure a task runs once even when the app runs on several servers. It needs a shared cache like Redis or the database.

  • timezone() sets the time zone for that task, so "7 in the morning" means the client's morning, not the server's.

  • runInBackground() lets slow tasks run side by side instead of waiting for each other.

Check what is scheduled

php artisan schedule:list shows every task and when it runs next. I run it after every deploy that touches the schedule. Locally, php artisan schedule:work runs the scheduler in the foreground so you can watch tasks fire without setting up cron.

Know when a task fails

A scheduled task that fails silently is worse than no task, because everyone assumes it ran. I use onFailure() to send myself a notification, and for important jobs I log a short line when they finish. If the daily report did not arrive, I want to know before the client does.

Keep the tasks thin

The scheduled command should mostly hand work off. If a task has to process thousands of records, the command can split them into queued jobs. The scheduler starts things on time, and the queue does the heavy lifting.