Skip to main content

Finish accepted HTTP requests during shutdown

Drain in-flight HTTP work on SIGTERM with readiness, keep-alive admission, and a finite application deadline under Bex's termination budget.

Share
Last updated on September 23, 2026
On this page

Web and private services lose traffic and receive SIGTERM when a release replaces them. An app that exits on the first signal drops requests that were already accepted. This page points at a downloadable sample that finishes bounded work, refuses new work while draining, and exits within a configured deadline.

Source pin: Bex 88f0a89792d705a079075c383cf0a5a9561acc06 (deployment_projection.go: 10s preStop.sleep for traffic-serving pods; workers and disk-attached Recreate services get no preStop; maxShutdownDelaySeconds is the post-SIGTERM budget, default 30s; pod grace = preStop + that delay). The pin documents source behavior — not which revision is deployed at api.bex.co.

What the sample does

The http-shutdown example:

  1. Listens on 0.0.0.0:$PORT with GET /readyz for readiness
  2. Serves bounded GET /work?ms= requests and an SSE GET /stream path
  3. On the first SIGTERM/SIGINT, enters draining once — readiness fails and new work (including on keep-alive sockets) returns 503
  4. Lets accepted work finish inside SHUTDOWN_DEADLINE_MS, then exits
  5. Forces unfinished streams/work when the deadline elapses

The application does not sleep for the platform preStop window. preStop runs before SIGTERM so endpoints can drain; your process budget starts at the signal.

Run locally

bash
curl -fsSLO https://bex.co/examples/http-shutdown-latest.zip
curl -fsSLO https://bex.co/examples/http-shutdown-latest.zip.sha256
shasum -a 256 -c http-shutdown-latest.zip.sha256
unzip http-shutdown-latest.zip
cd http-shutdown
npm test
npm run rehearse

Node 22+. No npm dependencies. npm test and npm run rehearse spawn a real child process and use sockets/signals; they clean up only that child.

Local success is not evidence of Traefik endpoint propagation or whole-service zero downtime.

Deploy on Bex

Use the sample render.yaml: type: web, healthCheckPath: /readyz, maxShutdownDelaySeconds: 30, and SHUTDOWN_DEADLINE_MS below that budget. Docker CMD is exec-form (["node","server.mjs"]) so Node is PID 1 and receives signals.

Workers and disk-backed Recreate services skip the 10s preStop drain at the pinned revision — they still get SIGTERM and the configured shutdown delay.

Troubleshooting

SymptomCheck
Readiness never passesHOST/PORT, process listening on 0.0.0.0, probe path /readyz
Requests cut off on deploySignal reaches the app (exec-form entrypoint); drain budget vs work duration
Process never exitsSHUTDOWN_DEADLINE_MS must bound streams; avoid ignoring SIGTERM
Still serving after SIGTERMExpected briefly for in-flight work; new work must 503 while draining

Was this page helpful?

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex