Skip to main content
Server & DevOpsOctober 8, 20264 min read

One Line in Laravel 13.35 Stops Duplicate Scheduled Jobs

Get technical support

The day-to-day nobody has time for

Scale a Laravel app to three servers and every scheduled task can run three times, from the morning report to the billing run. Laravel 13.35, released October 6, 2026, adds one setting that keeps every task on a single server, plus gentler deploys for long jobs.

The Email That Arrived Three Times

Picture a Laravel app that outgrows its first server and moves to three, or to three pods in Kubernetes. Everything looks fine until a customer forwards the same morning email, received three times. Then the nightly backup turns out to start three times at once, and the billing task can charge the same invoice three times.

The cause is simple. Every server runs the scheduler, so every server sees each task as due and runs it. The developer who added the new setting describes the same moment in the pull request, adding replicas and suddenly wondering why 50 copies of something are going out.

The Old Fix Depended on Remembering

Laravel has long had onOneServer(), which takes a lock in the shared cache so that only one server runs a task. It works, but it has to be added to every task, or the whole schedule has to be wrapped in a group. One new task without it, added by someone who did not know, and the duplicates are back.

The One Line

Laravel 13.35, released on October 6, 2026, adds Schedule::alwaysOnOneServer(). Call it once in the boot method of AppServiceProvider and every scheduled task behaves as if it had onOneServer().

use Illuminate\Support\Facades\Schedule;

public function boot(): void
{
    Schedule::alwaysOnOneServer();
}

It is off by default, so nothing changes until you add it. Two details matter before you rely on it.

  • The lock lives in your default cache, which has to be Redis, Memcached, DynamoDB or the database driver, shared by all servers. A file or array cache on each server cannot coordinate anything.
  • Scheduled closures without a name are skipped rather than locked, so give them a name with ->name() if they should run once too.

Two More Changes for Teams on Several Servers

Long scheduled jobs can now notice a deploy. php artisan schedule:interrupt, the command deploy scripts run to stop the scheduler, never reached a long command started with runInBackground(). A job that ran for an hour had no way to know a deploy wanted it to stop, so the deploy either waited for it or killed it halfway. 13.35 records when the interrupt happened, and the new Schedule::interruptedSince() lets the job stop at a safe point and leave the rest to its next run.

Queue workers can size their memory limit as a share of PHP's own limit. php artisan queue:work --memory=60% now means 60 percent of memory_limit, so the worker setting follows the container when you resize it instead of being recalculated by hand. Laravel Cloud's managed queues are not affected, since they do not take custom worker options.

php artisan schedule:list --json also includes onOneServer for every task now, so monitoring can confirm which tasks are meant to run once.

Upgrading

Moving from an earlier 13.x release is a composer update laravel/framework. Apps still below 13.30.0, or 12.69.0 on Laravel 12, also miss the fix for CVE-2026-102279, a low-severity XSS on the debug page. It only matters where APP_DEBUG is on, which a production server should never have.

Where It Fits

Duplicate jobs are one of the first surprises of scaling past one server. Our guide to setting up a dedicated Laravel worker server on Ubuntu covers the queue side, and the guide to automating Laravel deployments with GitHub Actions is the natural place to add the schedule:interrupt step. Running Laravel across several servers or containers is everyday work for our infrastructure management team.

Sources

Or read how we handle it in Infrastructure Management.