During a Laravel deploy that uses php artisan down, what happens to scheduled tasks, and what do evenInMaintenanceMode() and schedule:interrupt change?
answer
- down means not due
- no skip event, no catch-up
- evenInMaintenanceMode opts back in
- sub-minute loop keeps old code
- interrupt flag lasts to end of minute
basics
~20 sWhile the app is in maintenance mode, scheduled tasks are treated as not due and silently do not run, unless marked evenInMaintenanceMode(). schedule:interrupt tells an already running sub-minute schedule:run loop to stop early, so it does not keep executing the previous release.
solid answer
~50 s`schedule:run` checks maintenance mode when it picks due tasks: if the app is down, every task without `->evenInMaintenanceMode()` is simply not due — no `ScheduledTaskSkipped` event, and no catch-up after `php artisan up`. Use the opt-in only for tasks safe to run against a half-deployed app, such as health pings. The sub-minute loop re-checks maintenance mode and stops repeating non-opted tasks once the app goes down mid-minute. Separately, a `schedule:run` with sub-minute tasks lives until the end of its minute, so after a deploy it can keep running the old release. `php artisan schedule:interrupt` stores an interrupt flag in the cache until the end of the current minute; the loop checks it before each repeat and returns. Run it after the deploy finishes. In Laravel 13, `schedule:pause` and `schedule:resume` stop and restart all task processing without maintenance mode, with `evenWhenPaused()` as the opt-out.
code
bash · 6 linesphp artisan down
# ... pull code, composer install, php artisan migrate --force, rebuild caches ...
php artisan up
# Stop any sub-minute schedule:run loop still running the old release
php artisan schedule:interruptgo deeper
Know that scheduled tasks do not run while the app is down, unless a task is marked evenInMaintenanceMode().
Explain that down tasks are simply not due (no skip event, no catch-up) and what the sub-minute loop does when the app goes down mid-minute.
Put schedule:interrupt after deploys, share the cache it signals through, opt in only safe tasks, and plan for runs lost in the window.
Define how deploys interact with scheduled work: which tasks may run during maintenance, when to pause instead, and who reruns missed business tasks.
## Maintenance mode and the scheduler `php artisan down` puts the application into maintenance mode (how that mode works is its own topic). The scheduler reacts at the very first stage of each run: when `schedule:run` picks due tasks, an event whose `evenInMaintenanceMode` flag is not set is treated as **not due** while the application is down. Three consequences are easy to miss: - **Silence.** Because the task is filtered out before the filter stage, no `ScheduledTaskSkipped` event is dispatched and no hook fires. - **No catch-up.** When `php artisan up` runs at 02:05, the 02:00 renewal task does not run late; that minute is gone. - **Long deploys cost runs.** A deploy that keeps the app down for ten minutes skips ten minutes' worth of every task. ```php Schedule::job(new HealthPing) ->everyMinute() ->evenInMaintenanceMode(); ``` `evenInMaintenanceMode()` opts a task back in. Reserve it for work that is safe to run against a half-migrated database and half-deployed code: health pings, metrics, perhaps a queue-depth sample. A report generator or a billing run is exactly what maintenance mode is meant to hold back. ## Sub-minute tasks during a deploy When sub-minute tasks are due, `schedule:run` does not exit after its first pass; it loops until the end of the minute in which it started. Two deploy-related behaviours follow: 1. **Maintenance mid-minute.** Inside the loop the command re-checks maintenance mode before each repeat; once it has seen the app go down, tasks without `evenInMaintenanceMode()` stop repeating for the rest of that minute. 2. **Stale code.** A loop started at 12:00:00 keeps the PHP code and schedule it loaded — the previous release — until about 12:00:59, even if the new release went live at 12:00:20. ## schedule:interrupt ```bash php artisan schedule:interrupt ``` The command writes an `illuminate:schedule:interrupt` flag to the cache that expires at the end of the current minute. The sub-minute loop checks that flag before each repeat and returns as soon as it sees it, so the old process exits and the next cron tick starts a fresh `schedule:run` with the new code. Each new `schedule:run` that has repeatable tasks clears the flag when it starts. Practical points: - Add it **after** the deploy is complete, as the documentation recommends, so the next run loads the new release. - The deploy step and the scheduler hosts must share the application's default cache store, which is where the flag is written and read, or the loop never sees it. - It only affects the sub-minute repeat loop. Ordinary tasks already running in the foreground are not killed. - `Schedule::withoutInterruptionPolling()` turns off the cache checks for pause and interrupt entirely, for apps that do not want the scheduler to read the cache every repeat. ## Pausing without maintenance mode Laravel 13 added a gentler switch for "stop scheduled work for a while": | Command or method | Effect | | --- | --- | | `php artisan schedule:pause` | Stores a cache flag (with no expiry); `schedule:run` then skips every task not marked `evenWhenPaused()` | | `php artisan schedule:resume` (alias `schedule:continue`) | Removes the flag | | `->evenWhenPaused()` | Keeps a task running while paused | Unlike maintenance mode, pausing leaves the web application up, and paused tasks fire `ScheduledTaskSkipped`, so they are visible to listeners. ## A deploy runbook fragment 1. `php artisan down` if the release needs it (tasks without the opt-in stop being due). 2. Deploy code, run migrations, rebuild caches. 3. `php artisan up`. 4. `php artisan schedule:interrupt`, so any sub-minute loop from the old release exits now rather than at the end of its minute. 5. Check that time-critical tasks whose minute fell inside the window are handled — rerun them by hand if the business needs them.
- Why does Laravel's schedule:interrupt have no effect on a scheduler host that uses a different cache store from the deploy machine?The command only writes a flag to the cache; the running `schedule:run` loop reads the flag from its own cache store before each repeat. If the two processes use different stores, for example local file caches on different hosts, the loop never sees the flag and runs to the end of its minute.
- When would you use schedule:pause instead of maintenance mode in a Laravel 13 app?When scheduled work must stop but the web app should stay up — for example while a third-party billing API is down. `schedule:pause` sets a cache flag, `schedule:run` skips every task not marked `evenWhenPaused()`, the skips fire `ScheduledTaskSkipped`, and `schedule:resume` restores processing.
saying these in an interview costs you the question
- Scheduled tasks missed during maintenance mode run automatically after php artisan up.
- Maintenance mode only affects HTTP requests, never scheduled tasks.
- schedule:interrupt kills every scheduled command that is currently running.
- evenInMaintenanceMode() is a safe default for all scheduled tasks.
- Tasks skipped for maintenance mode fire ScheduledTaskSkipped events.