Liveness vs readiness

Orchestrators ask two different health questions. Liveness is 'is the process running'. Readiness is 'can it handle traffic right now'. They look similar, and most teams start out with only one endpoint. That is a mistake, because they answer different questions and require different responses.

Liveness failing means restart the container. Readiness failing means stop sending traffic to this instance, but do not restart it. A service that is still warming up its cache is ready to stay alive but not ready to serve traffic. Getting these mixed up means healthy instances get killed or broken instances keep receiving requests.

after/routes/health.routes.ts
typescript
import { Router } from 'express';
import { db } from '../db';

export const healthRouter = Router();

// Liveness: am I still a functioning process?
healthRouter.get('/health', (req, res) => {
  res.json({ status: 'ok' });
});

// Readiness: can I actually serve traffic right now?
healthRouter.get('/health/ready', async (req, res) => {
  try {
    await db.execute('SELECT 1');
    res.json({ status: 'ready' });
  } catch (err) {
    res.status(503).json({ status: 'not-ready', reason: 'db unreachable' });
  }
});

Two endpoints, two different answers to two different questions.

In Kubernetes, the liveness probe goes to /health and the readiness probe goes to /health/ready. The first restarts dead processes. The second takes unhealthy instances out of rotation without killing them. Same pattern works with any modern orchestrator, load balancer, or managed platform.

Quiz: Quiz

Loading practice…