
☸️ Simple to Scalable: Deploying a Node.js App on Kubernetes with Health Checks, Configs & Monitoring
This guide walks you through deploying a Node.js Express API to Kubernetes using Minikube, with clear, beginner-friendly steps. By the end, you’ll have a containerized app running in Kubernetes, complete with health checks, configuration management, scaling, and monitoring with Prometheus and Grafana.
🧱 1. Basic App Setup
Let’s start by setting up a simple Node.js Express API, containerizing it with Docker, and deploying it to a local Kubernetes cluster using Minikube.
Step 1: Create a Node.js Express API
Create a file named server.js for a basic Express API with health check endpoints.
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
const message = process.env.APP_MESSAGE || 'Hello, World!';
res.send(message);
});
app.get('/healthz', (req, res) => {
res.status(200).send('OK');
});
app.get('/readyz', (req, res) => {
res.status(200).send('Ready');
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
- Create a directory for your project:
mkdir node-api && cd node-api. - Initialize a Node.js project:
npm init -y. - Install Express:
npm install express. - Save the above
server.jscode in the project directory. - Test locally:
node server.jsand visithttp://localhost:3000in your browser.
Step 2: Containerize the App with Docker
Create a Dockerfile to build a Docker image for the app.
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
- Build the Docker image:
docker build -t node-api:1.0 . - Test the image locally:
docker run -p 3000:3000 node-api:1.0. - Push the image to a registry (e.g., Docker Hub) or load it into Minikube later.
Step 3: Set Up Minikube
Minikube creates a local Kubernetes cluster for testing.
- Install Minikube: Follow instructions at minikube.sigs.k8s.io.
- Start Minikube:
minikube start. - Load the Docker image into Minikube:
minikube image load node-api:1.0.
Step 4: Deploy to Kubernetes
Create Kubernetes manifests to deploy the app.
Deployment (node-api-deploy.yaml)
This file defines the app’s deployment, including the container and health checks.
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-api-deployment
labels:
app: node-api
spec:
replicas: 1
selector:
matchLabels:
app: node-api
template:
metadata:
labels:
app: node-api
spec:
containers:
- name: node-api
image: node-api:1.0
ports:
- containerPort: 3000
Service (node-api-service.yaml)
This exposes the app within the cluster.
apiVersion: v1
kind: Service
metadata:
name: node-api-service
spec:
selector:
app: node-api
ports:
- protocol: TCP
port: 3000
targetPort: 3000
type: ClusterIP
- Apply the manifests:
kubectl apply -f node-api-deploy.yaml kubectl apply -f node-api-service.yaml - Verify the deployment:
kubectl get podsandkubectl get services. - Access the app:
kubectl port-forward service/node-api-service 8080:3000. - Open
http://localhost:8080in your browser to see the app running.
⚙️ 2. Adding Health Checks
Health checks ensure Kubernetes knows if your app is running correctly (livenessProbe) or ready to serve traffic (readinessProbe).
Update the node-api-deploy.yaml to include probes:
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-api-deployment
labels:
app: node-api
spec:
replicas: 1
selector:
matchLabels:
app: node-api
template:
metadata:
labels:
app: node-api
spec:
containers:
- name: node-api
image: node-api:1.0
ports:
- containerPort: 3000
livenessProbe:
httpGet:
path: /healthz
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
- Apply the updated deployment:
kubectl apply -f node-api-deploy.yaml. - Verify probes:
kubectl describe pod <pod-name>to check probe status.
🔐 3. ConfigMaps & Secrets
Use ConfigMaps for non-sensitive configuration and Secrets for sensitive data.
Step 1: Create a ConfigMap
Create node-api-config.yaml for the app’s message.
apiVersion: v1
kind: ConfigMap
metadata:
name: node-api-config
data:
APP_MESSAGE: "Hello from ConfigMap!"
Step 2: Create a Secret
Create node-api-secret.yaml for a secret key.
apiVersion: v1
kind: Secret
metadata:
name: node-api-secret
type: Opaque
data:
SECRET_KEY: bXJTZWNyZXRWYWx1ZQ== # base64 encoded "mySecretValue"
- Apply both:
kubectl apply -f node-api-config.yaml kubectl apply -f node-api-secret.yaml
Step 3: Use ConfigMap and Secret in Deployment
Update node-api-deploy.yaml to inject these into the container.
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-api-deployment
labels:
app: node-api
spec:
replicas: 1
selector:
matchLabels:
app: node-api
template:
metadata:
labels:
app: node-api
spec:
containers:
- name: node-api
image: node-api:1.0
ports:
- containerPort: 3000
livenessProbe:
httpGet:
path: /healthz
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
env:
- name: APP_MESSAGE
valueFrom:
configMapKeyRef:
name: node-api-config
key: APP_MESSAGE
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: node-api-secret
key: SECRET_KEY
- Apply the updated deployment:
kubectl apply -f node-api-deploy.yaml. - Verify: Access
http://localhost:8080(after port-forwarding) to see the ConfigMap message.
📈 4. Scaling & Rolling Updates
Kubernetes makes scaling and updating apps easy.
Step 1: Scale the App
Increase the number of replicas:
kubectl scale deployment node-api-deployment --replicas=3
Verify: kubectl get pods to see three pods running.
Step 2: Perform a Rolling Update
Update the app to a new image version (e.g., node-api:1.1):
kubectl set image deployment/node-api-deployment node-api=node-api:1.1
Verify: kubectl rollout status deployment/node-api-deployment to ensure no downtime.
📊 5. Metrics & Monitoring
Monitor resource usage with Kubernetes Metrics Server, Prometheus, and Grafana.
Step 1: Install Metrics Server
- Install Metrics Server:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml - Verify:
kubectl top nodesandkubectl top podsto see resource usage.
Step 2: Install Prometheus
- Add the Prometheus Helm repository:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update - Install Prometheus in the
prometheusnamespace:kubectl create namespace prometheus helm install prometheus prometheus-community/prometheus --namespace prometheus - Access Prometheus UI:
kubectl -n prometheus port-forward svc/prometheus-server 9090. - Open
http://localhost:9090in your browser.
Step 3: Install Grafana
- Install Grafana via Helm:
helm repo add grafana https://grafana.github.io/helm-charts helm repo update helm install grafana grafana/grafana --namespace prometheus - Access Grafana:
kubectl -n prometheus port-forward svc/grafana 3000:80. - Open
http://localhost:3000and log in with default credentials (admin/admin).
📊 6. Visualizing Metrics in Grafana
Visualize your cluster’s metrics in Grafana.
- Log into Grafana (
http://localhost:3000). - Add a Prometheus data source:
- URL:
http://prometheus-server.prometheus.svc.cluster.local:80.
- URL:
- Import dashboards:
- Node Exporter Full (ID: 18603).
- Kubernetes Pods (ID: 6588).
- View real-time CPU and memory usage for nodes and pods.
✅ Project Status: Complete & Monitored
Congratulations! Your Node.js app is now:
- Containerized with Docker 🐳
- Running in Kubernetes with Minikube ☸️
- Health-monitored with liveness and readiness probes ✔️
- Configurable via ConfigMaps and Secrets 🔐
- Scalable and updatable with rolling updates 🔄
- Observed with Prometheus and Grafana 📈
This setup is a solid foundation for deploying Node.js apps to Kubernetes. Experiment further by adding more features or deploying to a cloud provider!