skip to content

In production, how do you build, start, health-check and restart Inertia's SSR server with Artisan, and what does --runtime change?

level: middleimportance: nice to knowfreq 20%

answer

  1. vite build plus vite build --ssr
  2. bundle in bootstrap/ssr
  3. inertia:start-ssr under a process monitor
  4. stop-ssr on deploy, --graceful
  5. check-ssr hits /health

basics

~20 s

Build the client and SSR bundles, run php artisan inertia:start-ssr under a process monitor, verify it with inertia:check-ssr, and on each deploy run inertia:stop-ssr so the monitor restarts it on the new bundle; --runtime picks node, bun or a binary path.

solid answer

~40 s

Production SSR needs two builds, `vite build && vite build --ssr` (the starter kits' `build:ssr` script), which writes the server bundle under `bootstrap/ssr`. `php artisan inertia:start-ssr` finds that bundle (or `inertia.ssr.bundle`), first calls `inertia:stop-ssr` silently, and then runs it with the runtime from `--runtime` or `inertia.ssr.runtime` (`INERTIA_SSR_RUNTIME`, default `node`); `bun` or an absolute binary path also work, and `ensure_runtime_exists` makes a missing binary fail fast. Because the command stays in the foreground, you run it under Supervisor or a similar monitor. On deploy, `inertia:stop-ssr` calls the server's `/shutdown` endpoint and the monitor restarts it with the new bundle; `--graceful` exits 0 when nothing was running. `inertia:check-ssr` calls `/health` and exits non-zero on failure, which suits a container health check.

code

bash · 8 lines
bash
# build both bundles
npm run build:ssr

# during the deploy: stop the old renderer; the process monitor restarts it
php artisan inertia:stop-ssr --graceful

# after the restart: fail the pipeline if SSR is not answering
php artisan inertia:check-ssr

go deeper

for a junior

Know that production SSR needs an extra build and a running process started with php artisan inertia:start-ssr.

for a middle

Explain bundle detection, the runtime option and its default, the port, and what stop-ssr, --graceful and check-ssr each do.

for a senior

Fit SSR into deploys and infrastructure: process monitors, restart on release, health checks, timeouts and containers without the bundle file.

for a principal

Decide how the SSR runtime is hosted and scaled, alongside the app or as its own service, and who owns its uptime and upgrades.

## Two bundles, one extra process An **Inertia** app with server-side rendering ships two JavaScript builds: - the **client bundle** in `public/build`, served to browsers; - the **SSR bundle** in `bootstrap/ssr`, run by a long-lived Node or Bun process that renders page components to HTML. The usual build is `vite build && vite build --ssr`; the React and Vue starter kits expose it as `npm run build:ssr`. With the `@inertiajs/vite` plugin the SSR entry is detected automatically (a `resources/js/ssr.*` file if present, otherwise the `app.*` entry is reused). In development none of this is needed: Inertia 3 renders through the Vite dev server. The commands below are for production. ## Starting the server `php artisan inertia:start-ssr` does the following: 1. refuses to run if `inertia.ssr.enabled` is false; 2. locates the bundle: `inertia.ssr.bundle` if set, otherwise the first existing file among `bootstrap/ssr/ssr.js`, `bootstrap/ssr/app.js`, their `.mjs` variants and two legacy `public/js` paths; 3. picks the **runtime**: the `--runtime` option, else `inertia.ssr.runtime` (env `INERTIA_SSR_RUNTIME`, default `node`); 4. if `ensure_runtime_exists` is true, fails when that binary cannot be found; 5. silently runs `inertia:stop-ssr` to clear an old instance; 6. starts `<runtime> <bundle>` with no timeout and streams its output, reporting stderr lines as exceptions. The server listens on port **13714** by default. Laravel reaches it at `inertia.ssr.url` (default `http://127.0.0.1:13714`). The bundle can enable Node's **cluster** mode to fork one worker per CPU on the same port. | `--runtime` value | Effect | |---|---| | omitted | uses `inertia.ssr.runtime`, `node` by default | | `bun` | runs the bundle with Bun | | `/usr/local/bin/node22` | runs a specific binary by absolute path | The docs require Node.js 22 or newer for the SSR server. ## Keeping it running `inertia:start-ssr` stays in the foreground, so production runs it under a **process monitor** such as Supervisor, systemd or the platform's daemon feature, configured to restart it when it exits. With the `pcntl` extension loaded, the command traps `SIGINT`, `SIGQUIT` and `SIGTERM` and stops the Node process it started, so the monitor can shut both down cleanly. ## Restarting on deploy The SSR process loads the bundle once. After a deploy that ships new page components, an old process would keep rendering old code, or fail to find new pages. So the deploy runs: - `php artisan inertia:stop-ssr`: sends a request to the server's `/shutdown` endpoint; the process exits and the monitor starts a fresh one on the new bundle; - `php artisan inertia:stop-ssr --graceful`: the same, but exits successfully when no server is running, which keeps first deploys and scripted pipelines green. ## Checking health `php artisan inertia:check-ssr` sends a GET to `/health` through the same HTTP client settings as rendering (including `inertia.ssr.timeout` and any `Inertia::configureSsrRequestUsing()` callback). It prints whether the server is running and exits non-zero when it is not, so it works as a container health check or a post-deploy smoke test. ## Multi-server and container setups - If web containers do not carry the SSR bundle file, set `inertia.ssr.ensure_bundle_exists` to false, otherwise the gateway skips SSR because it cannot see the bundle. - Point `INERTIA_SSR_URL` at the SSR service's address when it runs on another host. - Set `INERTIA_SSR_TIMEOUT` so a hung SSR server delays responses briefly and then falls back to client rendering. ## Common mistakes - building only the client bundle, so `start-ssr` reports the bundle missing; - deploying without `stop-ssr`, leaving a stale renderer; - running `start-ssr` in a shell with no monitor, so the first crash ends SSR for good.

  • Why is inertia:stop-ssr enough to load a new bundle?
    The command asks the running server to shut down through its `/shutdown` endpoint. The process monitor sees the exit and runs `inertia:start-ssr` again, which starts a new process that reads the freshly built bundle.
  • When would you set ensure_bundle_exists to false?
    When the web servers that run Laravel do not have the SSR bundle on disk, for example a separate SSR container. Otherwise the gateway checks for the file, finds nothing and quietly renders client-side only.

saying these in an interview costs you the question

  • inertia:start-ssr daemonises itself, so no process monitor is needed
  • A deploy picks up the new SSR bundle without restarting the process
  • --runtime selects between the React and Vue renderers
  • inertia:check-ssr rebuilds the SSR bundle when it is missing
  • The SSR server must run during local development too