You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+8-4Lines changed: 8 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -229,8 +229,8 @@ export default shipnode
229
229
|`.pkgManager(pm, opts?)`| auto-detected |`npm`\|`yarn`\|`pnpm`\|`bun`; `opts.installCommand` overrides the install command |
230
230
|`.installCommand(cmd)`| derived from pkg manager | Override the install command run on the server (e.g. `'npm ci --legacy-peer-deps'`). Equivalent to `pkgManager(pm, { installCommand: cmd })`|
231
231
|`.buildDir(dir)`| auto-detected | Frontend build output dir |
|`.zeroDowntime(altPort?)`|automatic for Caddy backends | Force blue-green releases and optionally choose the green port|
233
+
|`.noZeroDowntime()`| — |Opt out and recreate PM2 processes during deploy |
234
234
|`.healthCheck(path, opts?)`|`/health`, 30s, 3 retries | Post-deploy health check |
235
235
|`.noHealthCheck()`| — | Skip health check |
236
236
|`.envFile(f)`|`.env`| Local .env file to upload |
@@ -466,7 +466,11 @@ export default shipnode
466
466
467
467
## Zero-downtime releases
468
468
469
-
By default, shipnode uses a Capistrano-style release structure:
469
+
Backends with a domain and a PM2 web port use blue-green releases automatically. Shipnode starts the new web process on the idle port, checks it, and reloads Caddy only after it is healthy. Apps without a domain and worker-only apps keep the PM2 recreate path. Use `.noZeroDowntime()` to opt out explicitly; raw configuration may set `zeroDowntime: false`.
470
+
471
+
Blue-green keeps the previous and current web processes resident, so budget about **2× the web process memory**. On an eligible Caddy backend, `.zeroDowntime(altPort?)` remains available to force the mode and choose the alternate port (default: web port + 1).
472
+
473
+
Every app still uses a Capistrano-style release structure:
470
474
471
475
```
472
476
/var/www/myapp/
@@ -479,7 +483,7 @@ By default, shipnode uses a Capistrano-style release structure:
479
483
current -> releases/20240102T090000
480
484
```
481
485
482
-
PM2 is reloaded against `current/` after the symlink switch. If the health check fails, you can roll back instantly with `shipnode rollback`.
486
+
On the first migration, the new release starts on green while the existing uncoloured process keeps serving on blue. After health succeeds and Caddy reloads, Shipnode removes that legacy process. If health fails, Caddy and the legacy process remain untouched.
Copy file name to clipboardExpand all lines: docs/adr/0005-blue-green-zero-downtime.md
+4-6Lines changed: 4 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,8 +1,6 @@
1
1
# Blue-green releases via a two-port colour swap behind Caddy
2
2
3
-
The default deploy strategy stops the whole PM2 process set and starts the new release on the same port (`pm2 delete … && pm2 start …`). Between the delete and the moment the new process is listening — install relink, framework boot, first health probe — the port is closed and inbound requests are refused. For a plain web app that is a multi-second window of dropped requests on every deploy.
4
-
5
-
`zeroDowntime` opts a backend web app into blue-green releases that close that window.
3
+
Backend web apps behind Caddy (a domain plus a PM2 web port) use blue-green releases by default. Worker-only apps and backends without a domain retain the recreate strategy. `noZeroDowntime()` or raw `zeroDowntime: false` explicitly opts a Caddy backend out.
6
4
7
5
## Mechanism
8
6
@@ -13,7 +11,7 @@ Each deploy:
13
11
1. Stages the release and switches the `current` symlink (as always). The previously-started colour keeps running the old code in memory — the symlink move doesn't touch a running process.
14
12
2. Boots the **idle** colour on its own port (`pm2 start ecosystem.web.cjs`), reaping any stale same-colour instance first. The active colour is never touched. Workers are **not** reloaded yet.
15
13
3. Health-checks the new colour on **its** port and colour-suffixed pm2 name (`api-green`).
16
-
4. Only on success: reloads the single worker set against the new release, then rewrites the Caddy site to the new colour's port and `systemctl reload caddy` — a graceful reload that drains in-flight requests, so the flip drops nothing. Then persists the new active colour.
14
+
4. Only on success: reloads the single worker set against the new release, then rewrites the Caddy site to the new colour's port and `systemctl reload caddy` — a graceful reload that drains in-flight requests. Then persists the new active colour. On the first migration only, the legacy uncoloured process is removed after this sequence.
17
15
18
16
A failed health check throws before step 4: Caddy is untouched, workers stay on the previous release, the old colour is still serving, `current` is reverted to the previous release when one exists, and the failed colour is reaped by the next deploy. Zero user impact on a bad release — the main prize.
19
17
@@ -29,5 +27,5 @@ Because the previous colour is still running, rollback is an instant Caddy flip
29
27
30
28
-**~2× memory for the web app** — both colours are resident between deploys. Documented; the price of instant rollback.
31
29
-**The port pair is fixed at the first deploy** and persisted. Later changes to the web port or `altPort` in config are ignored until the state file is cleared, so a running colour is never silently re-homed.
32
-
-**Migration costs one restart.**The first `zeroDowntime` deploy deletes the pre-existing uncoloured web process (which holds the blue == web port) by name so the target colour can bind. After that, every deploy is zero-drop.
33
-
-**Opt-in, default off.**The recreate path is byte-for-byte unchanged, so existing deployments are unaffected until they set `zeroDowntime`.
30
+
-**The first migration targets green.**A pre-existing uncoloured process can continue serving on the configured blue port through health and the Caddy reload; it is cleaned up only after the flip succeeds.
31
+
-**Default on for Caddy backends.**Backends without a domain and worker-only apps keep recreate semantics. `.noZeroDowntime()` is the explicit builder opt-out.
0 commit comments