k3d runs k3s Kubernetes clusters inside Docker containers, making it the go-to tool for local development and CI pipeline clusters. Because k3d clusters are often short-lived, teams rarely bother with monitoring them — and that is exactly when something breaks at the worst time: a CI run that silently fails because the cluster stopped responding, or a staging environment that looks healthy but cannot schedule pods.
This tutorial covers uptime monitoring for k3d clusters using Vigilmon. We will walk through:
- Exposing the Kubernetes API server for external health checks
- Monitoring workloads running inside a k3d cluster
- Port-forwarding based monitoring for local development
- Webhook alerts for DOWN/UP events
Prerequisites
- k3d installed and a cluster running (
k3d cluster create mycluster) - kubectl configured to target the cluster
- A free account at vigilmon.online
Part 1: Expose the Kubernetes API health endpoint
The Kubernetes API server exposes a /healthz endpoint that returns ok when the control plane is healthy. In k3d, the API server is accessible from the host machine on a mapped port.
Find the API server port
k3d cluster list
# NAME SERVERS AGENTS LOADBALANCER
# mycluster 1/1 2/2 true
kubectl cluster-info
# Kubernetes control plane is running at https://0.0.0.0:6443
The port is configured when you create the cluster. If you used the default settings, the API server maps to a random host port. To use a fixed port:
k3d cluster create mycluster \
--api-port 6443 \
--port "8080:80@loadbalancer" \
--port "8443:443@loadbalancer"
Test the health endpoint
curl -k https://localhost:6443/healthz
# ok
The -k flag skips certificate verification for the self-signed k3d certificate. For monitoring purposes, you will want to either:
- Use a Vigilmon monitor configured to skip TLS verification, or
- Expose the API through an ingress with a valid certificate.
Part 2: Set up HTTP monitoring in Vigilmon
Monitor the Kubernetes API server
For a k3d cluster exposed on a public IP (staging, preview environment, or CI runner with an inbound rule):
- Log in to vigilmon.online and click Add Monitor.
- Choose HTTP(S) monitor.
- Enter:
https://your-server-ip:6443/healthz - Set interval to 1 minute.
- Add a keyword check: must contain
ok. - Toggle Skip TLS verification if using a self-signed certificate.
- Add your alert channel.
- Click Save.
Monitor workloads through the k3d load balancer
k3d includes a built-in load balancer (powered by Nginx) that routes traffic from host ports to cluster services. If your application exposes a health check, monitor it directly:
- Add another HTTP(S) monitor.
- Enter:
http://your-server-ip:8080/health - Set interval to 1 minute.
- Add a keyword check matching your application's health response.
- Click Save.
This catches the most common k3d failure modes: the cluster is running but your application pod crashed, or a misconfigured ingress rule stopped routing traffic.
Part 3: Add a lightweight health endpoint to your cluster
If your application does not expose a /health endpoint, deploy a simple health probe inside the cluster:
# k3d-health-probe.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: health-probe
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: health-probe
template:
metadata:
labels:
app: health-probe
spec:
containers:
- name: probe
image: nginx:alpine
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: health-probe
namespace: default
spec:
selector:
app: health-probe
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: health-probe-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- http:
paths:
- path: /probe
pathType: Prefix
backend:
service:
name: health-probe
port:
number: 80
Apply it:
kubectl apply -f k3d-health-probe.yaml
Then add a Vigilmon monitor for http://your-server-ip:8080/probe.
Part 4: CI pipeline monitoring with port forwarding
For ephemeral k3d clusters in CI pipelines, use a port-forward sidecar to expose the health endpoint during the test run:
# .github/workflows/integration-test.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install k3d
run: curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
- name: Create cluster
run: |
k3d cluster create testcluster \
--api-port 6443 \
--port "8080:80@loadbalancer" \
--wait
- name: Wait for cluster ready
run: |
kubectl wait --for=condition=ready nodes --all --timeout=60s
- name: Verify API health
run: |
curl -k https://localhost:6443/healthz
# Returns "ok" if cluster is healthy
- name: Deploy application
run: kubectl apply -f k8s/
- name: Run tests
run: npm test
- name: Delete cluster
if: always()
run: k3d cluster delete testcluster
For longer-running environments where you want Vigilmon to alert on CI runner failures, deploy a persistent health endpoint using the probe approach from Part 3.
Part 5: Monitor node and cluster status
k3d clusters can lose nodes when Docker containers are stopped or when the host runs out of memory. Add a node-health endpoint:
# Create a simple node checker as a CronJob
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: CronJob
metadata:
name: node-health-reporter
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
serviceAccountName: default
containers:
- name: reporter
image: bitnami/kubectl:latest
command:
- sh
- -c
- |
READY=$(kubectl get nodes --no-headers | grep -c ' Ready')
TOTAL=$(kubectl get nodes --no-headers | wc -l)
echo "Nodes ready: $READY/$TOTAL"
if [ "$READY" != "$TOTAL" ]; then exit 1; fi
restartPolicy: Never
EOF
Part 6: Webhook alerts
Configure Vigilmon to deliver alerts to your team when a k3d environment goes down:
// webhook-receiver.ts
import express from 'express';
const app = express();
app.use(express.json());
app.post('/webhook/vigilmon', (req, res) => {
const { monitor_name, status, url, checked_at, response_code } = req.body;
if (status === 'down') {
console.error('[K3D] Cluster monitor DOWN', {
monitor: monitor_name,
url,
code: response_code,
at: checked_at,
});
// Notify dev team — someone's local-dev or CI cluster is down
notifySlack({
channel: '#dev-infra',
text: `k3d cluster down: ${monitor_name} (${url})`,
});
} else if (status === 'up') {
console.info('[K3D] Cluster monitor recovered', { monitor: monitor_name });
}
res.sendStatus(204);
});
app.listen(3001);
Vigilmon sends this payload on state transitions:
{
"monitor_id": "mon_abc123",
"monitor_name": "k3d Staging API",
"status": "down",
"url": "https://staging.example.com:6443/healthz",
"checked_at": "2026-06-30T08:01:00Z",
"response_code": 0,
"response_time_ms": 5000
}
A response_code of 0 indicates a connection timeout — the API server is unreachable, not returning an error. This is the most common k3d failure mode when Docker runs out of memory.
Part 7: Multiple environments
If you run multiple k3d environments (dev, staging, feature branches), add a monitor per environment:
| Environment | Monitor URL | Name |
|-------------|-------------|------|
| Dev | https://dev.example.com:6443/healthz | k3d Dev Cluster |
| Staging | https://staging.example.com:6443/healthz | k3d Staging Cluster |
| Feature | http://feature.example.com:8080/health | k3d Feature App |
Tag them with k3d in Vigilmon for a consolidated cluster health dashboard.
Summary
Your k3d clusters now have three layers of monitoring:
- API server
/healthz— polled every 60 seconds, confirms the Kubernetes control plane is healthy. - Application health endpoint — a separate monitor through the k3d load balancer catches pod-level failures independent of the control plane.
- Webhook alerts — DOWN events reach your dev team channel within 60 seconds so nobody wastes time debugging a cluster that is silently offline.
Vigilmon handles check scheduling, multi-region polling, alert routing, and uptime history.
Monitor your k3d clusters free at vigilmon.online
#k3d #kubernetes #devops #monitoring #docker