Years ago I inherited a codebase with three web servers, one shared Redis and fourteen scheduled tasks. Thirteen of them had ->onOneServer() on the end. The fourteenth sent a weekly invoice digest, so every Monday at 06:00 some customers got the same email three times, a few seconds apart. Nobody had made a strange decision. Someone added a task on a Friday and copied it from a project that ran on a single box. When I read the Laravel 13.35 release notes, tagged on October 6, I skipped past the HTTP verb and went straight to Schedule::alwaysOnOneServer(), because that bug had cost me a weekend.

Here's my case: the most valuable changes in this release take something you had to repeat on every task or every request and turn it into one setting you make once. You call Schedule::alwaysOnOneServer() in a service provider and every scheduled event gets the single-server lock. You apply FormRequest::failOnUnknownFields() globally and every form request rejects input that its rules don't mention. As of 13.35, strict mode also ignores the _token and _method fields that Blade's @csrf and @method('PUT') add. Before that fix, a strict request failed on an ordinary PUT form, which made the global switch close to unusable for server-rendered apps. Now it fits the most common Laravel app there is.

These switches matter because remembering a rule on every call doesn't scale with the number of people writing code. One forgotten modifier in a pull request is enough, and a reviewer won't catch something that's missing. A global setting moves the decision from every place you write a task or a request to the one place your app boots. It also gives you something you can audit. schedule:list --json now has an on_one_server field for each event, so a ten-line CI check can fail the build when a task drifts. Before, that check meant grepping for a method call and hoping nobody had wrapped it in a helper.

The strongest objection deserves a fair hearing, and it isn't 'breaking changes are scary'. Global switches hide behavior. Once alwaysOnOneServer() is on, a task's own definition no longer tells you how it runs, and there are tasks you really do want on every node: clearing a local temp directory, rotating per-host logs, warming an OPcache-adjacent file cache. There's also a sharp edge in the implementation. Closures scheduled with Schedule::call() are skipped by the global switch unless they have a name, which follows the documented rule for single-server closures. But a developer reading the provider will assume 'always' means always. And the lock depends on a shared cache store: database, memcached, dynamodb or redis, reachable by every server. Turn this on with the file cache driver and you get no protection at all.

I accept all of that. My conclusion is that it argues for better escape hatches and louder failures, not for keeping the burden on the caller. A per-task way to say 'run this on every node', plus a boot-time warning when the global switch is on and the cache driver can't hold a shared lock, would fix most of it. The unnamed closure case should probably throw instead of quietly falling back to running on every server. I'd much rather have a framework that's safe by default and makes me opt out of safety on purpose than one where the safe path depends on my memory at 17:40 on a Friday.

The rest of the release supports the same argument from another direction. chunkById() and lazyById() used to loop forever on models with a cast primary key. withCasts() on a cloned query used to change the original. Reference cycles in PendingRequest::throw()'s default callback and in Mailable headers() and priority() callbacks quietly held memory in long-running workers. None of these got a headline, and every one of them is a trap you could only avoid by already knowing about it. The new opt-in pieces follow the same cautious pattern. lazy_root_creation for local disks defaults to false, and the HasDefaultAttributes trait does nothing until you add it. That's right for a minor version. Minor versions are for adding switches, and major versions are where you change which way they point.

So my wish for Laravel 14 is specific. Make single-server scheduling the default whenever the configured cache store supports locks, with an explicit per-task opt-out. Give new projects strict form requests out of the box, now that the Blade problem is gone. Leave the rest alone. Route::query() is nice to have, but client, proxy and CDN support for QUERY is still uneven, so it shouldn't drive anyone's upgrade. Run composer update laravel/framework this week anyway, and run your tests, especially if you use morphTo, HasOneThrough or JSON casts.

Now I'd like your take, especially from people running Laravel on more than one box. If alwaysOnOneServer() became the default tomorrow, which of your scheduled tasks would break because it needs to run on every server, and would you rather mark those tasks or the other ninety percent?