Health Check Project — Complete Implementation Guide
In this tutorial, you will learn about Health Check Project. We cover key concepts, practical examples, and best practices to help you master this topic.
This health check project guides you through building a comprehensive health check system for a production microservice, including multiple probe types, custom indicators, aggregation, and monitoring integration.
What You'll Learn
By the end of this project, you will have built a complete health check system that handles liveness, readiness, and startup probes, checks dependencies, exports metrics, and integrates with monitoring.
Why It Matters
A complete health check system is the foundation of production readiness. This project ties together every concept from previous lessons into a single deployable service.
Real-World Use
DodaTech uses this exact health check structure as the template for every new microservice. It includes all three Kubernetes probe types, dependency health, Prometheus metrics, and Slack alerting.
Project Learning Path
flowchart LR
A[Monitoring and Alerting] --> B[Health Check Project]
B --> C[Architecture]
B --> D[Implementation]
B --> E[Deployment]
B --> F{You Are Here}
style F fill:#f90,color:#fff
Project Architecture
The health check system consists of five components working together.
class ProjectArchitecture {
constructor() {
this.components = {
probeEndpoints: "Liveness, readiness, startup HTTP endpoints",
dependencyChecker: "Verifies database, cache, downstream APIs",
customIndicators: "Application-specific health logic",
metricsExporter: "Prometheus metrics for health data",
alertManager: "Slack notifications on state changes"
};
}
describe() {
Object.entries(this.components).forEach(([name, purpose]) => {
console.log(`${name}: ${purpose}`);
});
}
}
const arch = new ProjectArchitecture();
arch.describe();
// probeEndpoints: Liveness, readiness, startup HTTP endpoints
// dependencyChecker: Verifies database, cache, downstream APIs
// customIndicators: Application-specific health logic
// metricsExporter: Prometheus metrics for health data
// alertManager: Slack notifications on state changes
Core Health Server
The unified health server with all three probe types.
const http = require("http");
class HealthServer {
constructor(port = 8081) {
this.port = port;
this.state = {
started: false,
ready: false,
shuttingDown: false,
startTime: Date.now()
};
}
markStarted() { this.state.started = true; }
markReady() { this.state.ready = true; }
markShuttingDown() { this.state.shuttingDown = true; }
createHandler() {
return (req, res) => {
const response = {
timestamp: new Date().toISOString(),
uptime: Math.floor((Date.now() - this.state.startTime) / 1000)
};
if (req.url === "/healthz") {
response.status = "alive";
res.writeHead(200);
} else if (req.url === "/readyz") {
response.status = this.state.ready && !this.state.shuttingDown ? "ready" : "not ready";
res.writeHead(response.status === "ready" ? 200 : 503);
} else if (req.url === "/startupz") {
response.status = this.state.started ? "started" : "starting";
res.writeHead(response.status === "started" ? 200 : 503);
} else {
res.writeHead(404);
response.status = "not found";
}
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(response));
};
}
start() {
this.server = http.createServer(this.createHandler());
this.server.listen(this.port, () => {
console.log(`Health server on :${this.port}`);
});
}
stop() {
return new Promise(r => this.server.close(r));
}
}
const healthServer = new HealthServer();
healthServer.start();
setTimeout(() => healthServer.markStarted(), 3000);
setTimeout(() => healthServer.markReady(), 8000);
Dependency Checker Module
Centralized dependency checking with timeouts and circuit breaker awareness.
class DependencyChecker {
constructor() {
this.dependencies = [];
}
add(name, checkFn, options = {}) {
this.dependencies.push({
name,
check: checkFn,
timeout: options.timeout || 3000,
critical: options.critical !== false
});
}
async checkAll() {
const results = await Promise.allSettled(
this.dependencies.map(dep =>
Promise.race([
dep.check(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`${dep.name} timeout`)), dep.timeout)
)
]).then(result => ({ name: dep.name, ...result, critical: dep.critical, healthy: true }))
.catch(err => ({ name: dep.name, healthy: false, error: err.message, critical: dep.critical }))
)
);
return results.map(r => r.status === "fulfilled" ? r.value : r.reason);
}
async isReady() {
const results = await this.checkAll();
const criticalFailures = results.filter(r => r.critical && !r.healthy);
return {
ready: criticalFailures.length === 0,
checks: results
};
}
}
const checker = new DependencyChecker();
checker.add("database", async () => {
await new Promise(r => setTimeout(r, 10));
return { latencyMs: 10 };
}, { critical: true });
checker.add("cache", async () => {
await new Promise(r => setTimeout(r, 5));
return { latencyMs: 5 };
}, { critical: false });
checker.isReady().then(r => console.log("Ready:", r.ready));
Custom Health Indicators
Application-specific health checks for the project.
class CustomIndicators {
constructor() {
this.indicators = [];
}
add(name, checkFn) {
this.indicators.push({ name, check: checkFn });
}
async runAll() {
const results = [];
for (const { name, check } of this.indicators) {
try {
const result = await check();
results.push({
indicator: name,
status: "UP",
details: result
});
} catch (err) {
results.push({
indicator: name,
status: "DOWN",
error: err.message
});
}
}
return results;
}
}
const indicators = new CustomIndicators();
indicators.add("license-validity", async () => {
const daysUntilExpiry = 180;
if (daysUntilExpiry < 7) throw new Error("License expiring soon");
return { daysUntilExpiry };
});
indicators.add("data-freshness", async () => {
const lastUpdate = Date.now() - 3600000;
if (lastUpdate > 86400000) throw new Error("Data stale");
return { lastUpdate: new Date(lastUpdate).toISOString() };
});
indicators.runAll().then(r => console.log("Indicators:", r.length));
Metrics Integration
Prometheus metrics for the health check project.
const prometheus = require("prom-client");
class ProjectMetrics {
constructor() {
this.healthStatus = new prometheus.Gauge({
name: "project_health_status",
help: "Health status by component",
labelNames: ["component"]
});
this.healthDuration = new prometheus.Histogram({
name: "project_health_duration_seconds",
help: "Health check duration",
labelNames: ["component"],
buckets: [0.01, 0.05, 0.1, 0.5, 1]
});
this.dependencyStatus = new prometheus.Gauge({
name: "project_dependency_status",
help: "Dependency availability (1=up, 0=down)",
labelNames: ["dependency"]
});
}
recordHealth(component, healthy, durationMs) {
this.healthStatus.set({ component }, healthy ? 1 : 0);
this.healthDuration.observe({ component }, durationMs / 1000);
}
recordDependency(name, healthy) {
this.dependencyStatus.set({ dependency: name }, healthy ? 1 : 0);
}
}
const metrics = new ProjectMetrics();
metrics.recordHealth("database", true, 15);
metrics.recordHealth("cache", true, 3);
metrics.recordDependency("postgres", true);
console.log("Metrics initialized");
Putting It All Together
Combine all components into a unified health system.
class UnifiedHealthSystem {
constructor() {
this.healthServer = new HealthServer(8081);
this.dependencyChecker = new DependencyChecker();
this.customIndicators = new CustomIndicators();
this.metrics = new ProjectMetrics();
this.setupDependencies();
this.setupIndicators();
}
setupDependencies() {
this.dependencyChecker.add("database", async () => {
const start = Date.now();
await new Promise(r => setTimeout(r, 10));
const latency = Date.now() - start;
this.metrics.recordHealth("database", true, latency);
this.metrics.recordDependency("postgres", true);
return { latencyMs: latency };
});
}
setupIndicators() {
this.customIndicators.add("license", async () => {
return { valid: true, expiresIn: "180 days" };
});
}
async comprehensiveCheck() {
const depResult = await this.dependencyChecker.isReady();
const indicatorResults = await this.customIndicators.runAll();
return {
status: depResult.ready ? "UP" : "DOWN",
health: {
path: this.healthServer.state.ready ? "ready" : "not ready"
},
dependencies: depResult.checks,
customIndicators: indicatorResults,
timestamp: new Date().toISOString()
};
}
start() {
this.healthServer.start();
setInterval(async () => {
const result = await this.comprehensiveCheck();
if (result.status === "DOWN") {
console.warn("Health system reports DOWN");
}
}, 15000);
}
}
const system = new UnifiedHealthSystem();
system.start();
console.log("Unified health system started");
Common Mistakes
Not testing the complete health flow -- Each component (server, checker, indicators, metrics) must integrate correctly. Write integration tests that exercise the full chain.
Making health checks blocking -- The health endpoint must return quickly. All dependency checks must have timeouts. Long-running checks should be cached.
Exposing internal details in production -- Health responses can reveal dependency topology. Use show-details carefully in production environments.
Not handling the shutdown state -- During shutdown, readiness should return 503 but liveness should still return 200 to prevent unnecessary restarts.
Forgetting to register metrics -- Prometheus metrics must be registered before use. Unregistered metrics silently fail.
Practice Questions
What are the five components of the health check project? Probe endpoints, dependency checker, custom indicators, metrics exporter, and alert manager.
Why does the health server use a separate port (8081) from the application? To ensure health checks remain available even if the main application port is overloaded or closed during shutdown.
How do custom indicators differ from dependency checks? Custom indicators check application-specific concerns (license validity, data freshness). Dependency checks verify infrastructure components.
Challenge: Extend the project to support health check push notifications to a monitoring service.
class PushGatewayNotifier {
constructor(pushGatewayUrl) {
this.url = pushGatewayUrl;
}
async push(healthResult) {
const metrics = [
`health_status{service="health-system"} ${healthResult.status === "UP" ? 1 : 0}`,
`health_dependencies_total ${healthResult.dependencies?.length || 0}`,
`health_dependencies_unhealthy ${healthResult.dependencies?.filter(d => !d.healthy).length || 0}`
];
try {
await fetch(this.url, {
method: "POST",
body: metrics.join("\n")
});
} catch (err) {
console.error("Push gateway error:", err.message);
}
}
}
FAQ
Mini Project
The project is complete. Deploy it to a Kubernetes cluster and verify:
- All three probe types work correctly
- Dependency failures cause readiness to return 503
- Prometheus metrics are exported
- Alerts fire when critical dependencies fail
What's Next
Congratulations on completing the health check project. Next, learn about environment configuration to manage application settings across different environments.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro