Skip to content
LaravelBlog

laravel/framework: [13.x] schedule:list --json now reports on_one_server

LaravelBlogBot
LaravelBlogBot

Contributed by MizouziE

Part of the framework v13.35.0 release

Laravel 13.x picks up a small but genuinely useful addition to the scheduler's introspection tooling: schedule:list --json now reports which of your scheduled tasks are pinned to run on only one server. PR #61798 adds a single on_one_server boolean to each entry in the JSON payload, mirroring the onOneServer() method you can already chain onto any scheduled event. If you're upgrading from v13.34.0, this lands as a purely additive change — nothing to migrate, nothing to configure — but it closes a real observability gap for anyone running the scheduler across multiple machines.

What changed

The diff is a single line in the displayJson() method of ScheduleListCommand:

'on_one_server' => $event->onOneServer,

Every object in the JSON array produced by php artisan schedule:list --json now ends with the new key:

"environments": [],
"on_one_server": true

The flag is true when the event was defined with ->onOneServer() and false otherwise. The human-readable table view of schedule:list is untouched; this only affects the --json output. The author also considered gating the field behind -vv (the way route:list decides how much lands in its JSON), but it ships always present, following the same additive pattern used when environments was added to this payload in #57741.

Why it matters

In a multi-server deployment, every box runs php artisan schedule:run each minute, and onOneServer() uses a cache-backed lock — Redis, database, Memcached, DynamoDB, any store shared by all servers — so only one machine actually executes the task while the rest skip it.

Until now, the JSON output couldn't tell you which tasks were pinned that way. The payload already included has_mutex, but that answers a different question: "does the lock exist right now?" — point-in-time runtime state. on_one_server answers "is this event supposed to run on a single server?" — configuration intent — and that question previously required reading your schedule definition by hand.

Concretely, this helps with ops dashboards, config-drift checks between staging and production, and verifying after a refactor that critical jobs like backups or digest emails are still single-server pinned.

Seeing it in practice

// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('backup:run')->dailyAt('02:00')->onOneServer();

Schedule::call(fn () => Report::dispatch())
    ->name('quarterly-report')
    ->quarterly()
    ->onOneServer();

Schedule::command('inspire')->everyMinute();
php artisan schedule:list --json | jq -c '.[] | {command, on_one_server}'
{"command":"php artisan backup:run","on_one_server":true}
{"command":"php artisan report:generate","on_one_server":true}
{"command":"php artisan inspire","on_one_server":false}

Because it's plain JSON, it slots straight into CI as a guardrail:

# fail the build if backup:run ever loses its single-server pin
php artisan schedule:list --json \
  | jq -e '.[] | select(.command | contains("backup:run")) | .on_one_server == true'

What the diff and tests cover

Beyond the one-line payload addition, the PR extends the integration test suite. The existing testDisplayScheduleAsJson now asserts that the default value is false, so events without the flag report it explicitly rather than omitting it. A new test, testDisplayScheduleAsJsonWithOnOneServerData, schedules three events — an artisan command with ->onOneServer(), a command without it, and a named closure with the flag — and asserts the resulting column is exactly [true, false, true]. That confirms the field works for closures as well as commands, and that ordering in the output tracks the schedule definition.

Upgrade impact

For anyone moving from v13.34.0 to a release containing this change, there is essentially nothing to do:

  • No required changes. No new configuration, no migrations, and no behavioral change to how the scheduler runs or locks tasks. The CLI table output is byte-for-byte identical.
  • Consumers of the JSON payload should tolerate the new key. If you have strict schema validators, snapshot tests, or tooling that asserts an exact key set on this output, you'll need a one-line update to expect on_one_server. Consumers that already survived the environments addition will be fine.
  • The key is always present, not verbosity-gated, so you can rely on it unconditionally in scripts.

The upstream discussion also flags follow-up ideas — making has_mutex more readable by exposing without_overlapping and expires_at, and possibly a mutex_name field matching what schedule:finish accepts — but those were deliberately left out to keep this change minimal.

Takeaways

  • schedule:list --json now includes an on_one_server: true|false boolean for every scheduled event.
  • It reflects declarative configuration (->onOneServer()), complementing the runtime-state has_mutex field.
  • Purely additive and safe to upgrade to from 13.34.0 — no config, migrations, or behavior changes.
  • Update strict JSON schema checks or snapshot tests to expect the new key.
  • Works for both commands and closures; the flag is only meaningful when your servers share a cache backend for the lock.

Sources

More from this release

Related Articles