Skip to content
/practice·graceful-shutdown

Graceful Shutdown

beginnerlifecyclesignalsconnectionshttp
/concept
Graceful Shutdown
Stopping servers without dropping requests
Read full guide

Problem

Your server needs to shut down cleanly when it receives a termination signal. During shutdown, it must finish processing in-flight requests, stop accepting new connections, close database connections, and then exit — all without dropping any work.

Background

In production, servers are constantly being restarted — during deployments, scaling events, or infrastructure maintenance. When a container orchestrator (Kubernetes, ECS, Docker) wants to stop your server, it sends SIGTERM and waits a grace period before force-killing with SIGKILL. If your server doesn't handle SIGTERM properly, in-flight requests get dropped, database connections leak, and data can be corrupted.

A well-implemented graceful shutdown is one of the simplest but most impactful reliability patterns you can add to any server.

Requirements

  • Listen for SIGTERM and initiate a graceful shutdown
  • Stop accepting new TCP connections immediately when shutdown begins
  • Allow in-flight requests to complete (up to a reasonable timeout)
  • Close the database connection pool after all requests finish
  • Exit the process with code 0 after cleanup is complete
  • GET /health should return 503 once shutdown has been initiated (so load balancers stop sending traffic)
  • POST /work should insert a row into the jobs table and return 201 — this is the endpoint that must not drop requests during shutdown

Your Server

  • Your server receives PORT (default 3000) and DATABASE_URL environment variables
  • You must create the jobs table yourself on startup
  • The harness will send SIGTERM to your process during testing

API Contract

`GET /health`

Normal response:

  • Status: 200
  • Body: { "status": "ok" }

During shutdown:

  • Status: 503
  • Body: { "status": "shutting_down" }

`POST /work`

Create a job record.

Request:

  • Body: { "name": "string" }

Response:

  • Status: 201
  • Body: { "id": "", "name": "string" }

Useful APIs

These are the key APIs you'll need. How you combine them is the challenge.

code
class=class="text-pass">"text-fg-subtle">// Listen for a process signal
process.on(class="text-pass">'SIGTERM', () => { ... })

class=class="text-pass">"text-fg-subtle">// Stop accepting new connections (callback fires when existing connections close)
server.close(callback)

class=class="text-pass">"text-fg-subtle">// Close a pg connection pool
await pool.end()

class=class="text-pass">"text-fg-subtle">// Exit the process with a status code
process.exit(0)

class=class="text-pass">"text-fg-subtle">// Save a reference to the server (you'll need it for server.close)
const server = app.listen(PORT, () => { ... })
Scenarios·4 visible + hidden

Your solution will be tested against these scenarios plus a hidden set.

  1. 01POST /work creates a job and returns 201 with {id, name}
  2. 02GET /health returns 200 with {status: 'ok'} during normal operation
  3. 03In-flight requests complete successfully after SIGTERM is sent
  4. 04GET /health returns 503 after SIGTERM is sent

Hints

Click each hint to reveal it. Take your time — try before you peek.