Health Check Endpoints Explained — Complete Beginner's Guide
In this tutorial, you will learn about Health Check Endpoints Explained. We cover key concepts, practical examples, and best practices to help you master this topic.
Health check endpoints are HTTP endpoints that report whether a service is running correctly, enabling Orchestration systems and load balancers to make automated decisions about traffic routing and restarts.
What You'll Learn
By the end of this tutorial, you will understand what health check endpoints are, the difference between readiness and liveness probes, and how they enable self-healing infrastructure.
Why It Matters
Without health checks, orchestrators and load balancers operate blind. A crashed service still receives traffic, a restart never triggers, and operators must manually detect and respond to failures.
Real-World Use
DodaTech's Kubernetes cluster runs 200+ Microservices, each with at least two health endpoints. The platform team monitors aggregate health across all services on a single dashboard.
Health Check Learning Path
flowchart LR
A[Graceful Shutdown Project] --> B[Health Check Endpoints]
B --> C[Readiness vs Liveness]
B --> D[Implementation]
B --> E[Kubernetes]
B --> F{You Are Here}
style F fill:#f90,color:#fff
What Is a Health Check Endpoint?
A health check endpoint is a dedicated HTTP URL that returns the status of a service. It typically returns a 200 status when healthy and a non-200 status when unhealthy.
const http = require("http");
function createBasicHealthEndpoint(port = 8080) {
const server = http.createServer((req, res) => {
if (req.url === "/healthz") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({
status: "healthy",
timestamp: new Date().toISOString()
}));
} else {
res.writeHead(404);
res.end();
}
});
server.listen(port, () => {
console.log(`Health endpoint at http://localhost:${port}/healthz`);
});
return server;
}
createBasicHealthEndpoint();
// Health endpoint at http://localhost:8080/healthz
// GET /healthz -> {"status": "healthy", "timestamp": "2026-06-28T12:00:00.000Z"}
Readiness vs Liveness Probes
There are three types of health probes, each with a different purpose.
| Probe Type | Purpose | What Happens on Failure |
|---|---|---|
| Readiness | Is the service ready to receive traffic? | Removed from load balancer |
| Liveness | Is the service alive and running? | Container is restarted |
| Startup | Has the service finished starting? | Delays readiness/liveness checks |
class ProbeTypes {
static explain() {
const probes = [
{
name: "Readiness",
endpoint: "/readyz",
healthyResponse: "200 OK",
unhealthyResponse: "503 Service Unavailable",
action: "Stop routing traffic to this instance"
},
{
name: "Liveness",
endpoint: "/healthz",
healthyResponse: "200 OK",
unhealthyResponse: "503 Service Unavailable",
action: "Restart the container"
},
{
name: "Startup",
endpoint: "/startupz",
healthyResponse: "200 OK",
unhealthyResponse: "503 Service Unavailable",
action: "Delay other probes until ready"
}
];
probes.forEach(p => {
console.log(`${p.name} (${p.endpoint}): ${p.action}`);
});
}
}
ProbeTypes.explain();
// Readiness (/readyz): Stop routing traffic to this instance
// Liveness (/healthz): Restart the container
// Startup (/startupz): Delay other probes until ready
Why Health Checks Matter
Health checks enable automated recovery and zero-downtime operations.
function simulateWithoutHealthChecks() {
let serviceHealthy = false;
let crashedTime = Date.now();
let downtime = 0;
for (let minute = 0; minute < 60; minute++) {
if (!serviceHealthy) {
downtime++;
}
if (minute === 5) {
console.log("Service crashed at minute 5");
console.log("No health check to detect or restart");
}
if (minute === 45) {
console.log("Operator manually detects failure at minute 45");
serviceHealthy = true;
}
}
console.log(`Total downtime: ${downtime} minutes`);
}
function simulateWithHealthChecks() {
let serviceHealthy = false;
let crashedTime = Date.now();
let downtime = 0;
let healthCheckInterval = 1;
for (let minute = 0; minute < 60; minute++) {
if (!serviceHealthy) {
downtime++;
}
if (minute === 5) {
console.log("Service crashed at minute 5");
serviceHealthy = false;
}
if (minute === 6 && !serviceHealthy) {
console.log("Health check detected failure at minute 6");
console.log("Orchestrator restarts service");
serviceHealthy = true;
crashTime = 0;
}
}
console.log(`Total downtime: ${downtime} minutes`);
}
simulateWithoutHealthChecks();
simulateWithHealthChecks();
// Total downtime: 40 minutes (without health checks)
// Total downtime: 1 minute (with health checks)
Common Mistakes
Using the same endpoint for readiness and liveness -- Readiness and liveness serve different purposes. A service can be alive but not ready (e.g., during startup or shutdown).
Not including dependency status -- A health check that returns healthy when the database is down creates false confidence. Check critical dependencies.
Making health checks too expensive -- Health checks run every few seconds. Don't make expensive database queries or complex computations in health check handlers.
Not logging health check failures -- When a health check fails, log why. This helps operators diagnose whether failures are transient or persistent.
Returning 200 for all cases -- A health check should return a non-200 status when unhealthy. 503 is the standard status for unhealthy services.
Practice Questions
What is the difference between readiness and liveness probes? Readiness controls traffic routing (remove from service). Liveness controls restart behavior (restart container).
Why should health check endpoints be lightweight? They're called frequently (every 5-15 seconds). Expensive health checks increase load and can cause cascading failures.
What HTTP status code should a healthy endpoint return? 200 OK. Unhealthy endpoints should return 503 Service Unavailable.
Challenge: Implement a health check that returns different status codes for liveness, readiness, and startup probes.
const http = require("http");
function createProbeAwareServer() {
let ready = false;
let alive = true;
setTimeout(() => { ready = true; }, 5000);
return http.createServer((req, res) => {
const body = JSON.stringify({
status: "ok",
probes: { ready, alive }
});
if (req.url === "/healthz") {
res.writeHead(alive ? 200 : 503);
} else if (req.url === "/readyz") {
res.writeHead(ready ? 200 : 503);
} else if (req.url === "/startupz") {
res.writeHead(ready ? 200 : 503);
} else {
res.writeHead(404);
}
res.end(body);
});
}
const server = createProbeAwareServer();
server.listen(8080);
FAQ
Mini Project
Build a basic health check server with three endpoints (liveness, readiness, startup) that returns structured JSON with status codes, timestamps, and dependency status.
const http = require("http");
function buildHealthServer() {
const state = {
started: Date.now(),
ready: false,
healthy: true,
dependencies: { database: true, cache: true }
};
setTimeout(() => { state.ready = true; }, 3000);
const server = http.createServer((req, res) => {
const response = {
service: "my-app",
version: "1.0.0",
timestamp: new Date().toISOString(),
uptime: Math.floor((Date.now() - state.started) / 1000)
};
if (req.url === "/healthz") {
const alive = state.healthy;
res.writeHead(alive ? 200 : 503);
response.status = alive ? "alive" : "dead";
} else if (req.url === "/readyz") {
res.writeHead(state.ready ? 200 : 503);
response.status = state.ready ? "ready" : "not ready";
} else if (req.url === "/startupz") {
res.writeHead(state.ready ? 200 : 503);
response.status = state.ready ? "started" : "starting";
} else {
res.writeHead(404);
response.status = "not found";
}
response.dependencies = state.dependencies;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(response));
});
return server;
}
buildHealthServer().listen(8080);
console.log("Health check server started on :8080");
What's Next
Now that you understand health check basics, learn the difference between readiness and liveness probes in detail. Then explore different types of health checks.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro