Let's cut the fluff. Here is exactly what you need to know about this concept to survive in a real production environment.
1The Running Lie
Look, if you've ever dealt with this in production, you know exactly what the problem is. Just because a container's status says 'Running' does NOT mean your application is actually working. PID 1 might be executing, but your Node.js server could be deadlocked, or your database could be stuck indexing and refusing connections. If Docker only monitors the PID, it will blindly send user traffic to a frozen application. We need a way to tell Docker to monitor the actual health of the software inside. This isn't just academic theory—understanding the *why* behind this is what separates junior devs from senior engineers. When you deploy to a cluster, this is the mechanic that prevents catastrophic failure.
> docker ps
CONTAINER ID STATUS
5a4b3c2d1e0f Up 5 minutes (Running)
# But users see:
# HTTP 504 Gateway Timeout!
Status: OK
Success: Operation completed.
2Docker Healthchecks
Look, if you've ever dealt with this in production, you know exactly what the problem is. To solve this, we use the HEALTHCHECK instruction inside our Dockerfile. This tells the Docker Daemon to execute a specific command on a recurring schedule (e.g., every 30 seconds). If the command succeeds (Exit Code 0), Docker marks the container as 'Healthy'. If the command fails (Exit Code 1), Docker marks it as 'Unhealthy'. This allows Docker to make intelligent routing decisions based on the actual application state. This isn't just academic theory—understanding the *why* behind this is what separates junior devs from senior engineers. When you deploy to a cluster, this is the mechanic that prevents catastrophic failure.
FROM node:18-alpine
# ... setup app ...
# Ask the app if it is alive every 30s
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/health || exit 1
Status: OK
Success: Operation completed.
3Building the /health Endpoint
Look, if you've ever dealt with this in production, you know exactly what the problem is. For a Healthcheck to be effective, your application MUST provide an endpoint specifically for Docker to query. In a web API, you typically build a /health or /ping route. When Docker hits this route, your code shouldn't just return 'OK'. It should proactively check its own database connections and cache connectivity. If the database is down, the /health route should return HTTP 500, explicitly telling Docker: 'I am unhealthy'. This isn't just academic theory—understanding the *why* behind this is what separates junior devs from senior engineers. When you deploy to a cluster, this is the mechanic that prevents catastrophic failure.
app.get('/health', async (req, res) => {
try {
// Check if DB is actually alive
await db.ping();
res.status(200).send('OK');
} catch (error) {
// Tell Docker we are broken!
res.status(500).send('UNHEALTHY');
}
});
The server returned a 200 OK HTTP response.
