skip to content

After deploying new code to a Laravel Octane server, why do you run octane:reload, and when do you need a full restart instead?

level: middleimportance: must knowfreq 55%

answer

  1. old code lives in worker memory
  2. graceful worker restart, same server
  3. SIGUSR1, rr reset, admin API
  4. server-level options need a restart
  5. octane:status exits 0 when running

basics

~20 s

Octane workers keep the old code in memory, so a deploy is ignored until they restart; octane:reload restarts them gracefully inside the running server. Options fixed at start - port, worker count, max_execution_time, the driver - need a stop and start.

solid answer

~40 s

Octane workers boot the application once, so new files on disk change nothing until the workers restart. `php artisan octane:reload` restarts them gracefully without stopping the server: on Swoole it sends `SIGUSR1` to the master process, on RoadRunner it runs `rr reset` over the RPC port, and on FrankenPHP it re-sends the worker config to Caddy's admin API. The server process, its port and its pool settings survive a reload, so a changed worker count, port, `max_execution_time` or server driver needs `octane:stop` and a fresh `octane:start` - in production usually a restart through the process monitor, which runs `octane:start` in the foreground. `octane:status` prints whether the server is running and exits 0 if it is, 1 if not.

code

bash · 11 lines
bash
# Code-only release: new workers, same server
if php artisan octane:status; then
    php artisan octane:reload
else
    echo "Octane is not running" >&2
    exit 1
fi

# Release that changes --workers or max_execution_time:
# restart the monitored program instead of reloading
supervisorctl restart octane:*

go deeper

for a junior

Recall that Octane keeps code in memory, so every deploy ends with octane:reload, and that octane:stop and octane:status exist.

for a middle

Explain what reload does per server (SIGUSR1, rr reset, admin API), what survives it, and which changes need a stop and start.

for a senior

Show a deploy that checks status, reloads for code, restarts through the process monitor for server options, and never kills Swoole mid-traffic by accident.

for a principal

Weigh in-place worker reloads against replacing whole instances behind a load balancer, trading simplicity for zero dropped requests and easy rollback.

## Why a deploy needs a reload With **Laravel Octane**, each worker boots the application once and keeps it in memory: the service providers have run, the configuration is loaded, the route table is built and PHP's own compiled code is loaded. When a deploy puts new files on disk, the running workers do not notice. They keep answering with the code they booted with until they restart. So the last step of rolling code out under Octane is always to **replace the workers**. (Which other deploy steps come before it - caching config and routes, migrations - is a deployment topic.) ## `octane:reload`: new workers, same server `php artisan octane:reload` reads the running server's state file (`storage/logs/octane-server-state.json` by default) and asks the server to restart its workers. If no server is running it prints "Octane server is not running." and exits 1. Each driver does the restart its own way: | Server | What `octane:reload` does | |---|---| | Swoole / Open Swoole | sends `SIGUSR1` to the master process, Swoole's signal for a graceful worker restart | | RoadRunner | runs the `rr` binary's `reset` command against the RPC address recorded at start | | FrankenPHP | reads the `frankenphp` app config from Caddy's admin API and PATCHes it back, which restarts the workers | In all three cases the **server process stays up**: the listening socket, the port, the pool size and other options given at start are unchanged. New workers boot the new code and serve subsequent requests. That is why the docs describe reload as the step to run "after deployment". ## When a reload is not enough Anything the server received when it started survives a reload. Plan a full stop and start when you change: - the **port or host** Octane binds to; - `--workers` (and, on Swoole, `--task-workers`) or `--max-requests`; - `max_execution_time` in `config/octane.php` - Octane's docs say explicitly that changing it requires restarting the server; - the **server driver** itself, for example moving from RoadRunner to FrankenPHP; - the server binary or PHP extension (a new FrankenPHP or `rr` binary, a Swoole upgrade). ## `octane:stop` and how a restart really happens `php artisan octane:stop` also reads the state file and stops the server, deleting the state file afterwards: 1. **Swoole**: sends `SIGKILL` to the master, the manager and every worker process it finds, so in-flight requests are cut off. 2. **RoadRunner**: sends `SIGTERM` to the `rr` master process. 3. **FrankenPHP**: POSTs to the admin API's `/stop` endpoint. In production Octane runs under a **process monitor**; Octane's docs show a Supervisor program whose command is `php artisan octane:start ...`. `octane:start` stays in the foreground and handles `SIGINT`, `SIGTERM` and `SIGHUP` by calling `octane:stop` for its server. So the usual full restart is "restart the monitored program": the monitor signals `octane:start`, Octane stops the server, and the monitor starts it again with the current options. Running `octane:stop` by hand under a monitor with auto-restart simply leads to the monitor starting it again. ## `octane:status` in scripts `php artisan octane:status` prints "Octane server is running." or "Octane server is not running." and returns exit code **0 when running, 1 when not**, so a deploy script can guard a reload with it. Like the other commands, it uses `--server` or `config('octane.server')`, which must match the server actually running. ## The state file Every Octane command finds the running server through a JSON **state file** written at start: the master process ID (and, on Swoole, the manager ID), the host and port, the RPC or admin port, and a copy of the Octane config. Its path is `config('octane.state_file')`, which reads `OCTANE_STATE_FILE` and defaults to `storage/logs/octane-server-state.json`. Two consequences follow. Two Octane servers from one codebase on one host need different state files, or their commands will act on each other. And a release process that replaces the `storage` directory loses the file, after which `octane:reload` reports that the server is not running even though it is. ## A deploy sequence - finish putting the release in place (dependencies, cached config and routes); - `php artisan octane:status` to confirm the server is up; - `php artisan octane:reload` for a code-only release; - restart the monitored program instead when the release changes server options, the driver or the binary. ## Mistakes worth avoiding - Deploying and never reloading, then wondering why the old code still answers. - Expecting a reload to apply a new worker count or time limit. - Running `octane:stop` on Swoole during traffic as if it were graceful. - Calling these commands with a different `OCTANE_SERVER` than the running server, which makes them inspect the wrong state.

  • Is octane:stop on Swoole graceful?
    No. Octane's Swoole stop sends `SIGKILL` to the master, the manager and each worker, so requests in flight are dropped. RoadRunner receives `SIGTERM` and FrankenPHP a stop call on its admin API. If you need a graceful code swap, use `octane:reload`; plan full restarts for low-traffic moments or behind a load balancer.
  • Why can octane:reload say the server is not running when it clearly is?
    The commands find the server through `config('octane.server')` and that driver's state file. If `OCTANE_SERVER` differs from the running driver, or `state_file` points elsewhere, Octane inspects the wrong state. Pass `--server` matching the running server, or fix the env value.
  • Does a worker reload pick up a changed .env or config file?
    The new workers boot the application again, so they read configuration at boot. If configuration is cached, they read the cache file, so re-cache before reloading. Settings the server received at start, like the worker count or `max_execution_time`, still need a full restart.

saying these in an interview costs you the question

  • New PHP files are picked up automatically, because PHP recompiles every request
  • octane:reload stops the server and starts it again with the new options
  • A reload applies a changed --workers value or max_execution_time
  • octane:stop drains in-flight requests gracefully on every server
  • octane:status always exits 0 and only the printed text differs