ingress-nginx is the most widely deployed Kubernetes Ingress controller. It proxies external HTTP and HTTPS traffic into your cluster using NGINX under the hood, handles TLS termination, path-based routing, and rate limiting — all driven by Kubernetes Ingress resources. When ingress-nginx breaks, every application behind it goes dark simultaneously.
This tutorial shows you how to set up external uptime monitoring for ingress-nginx using Vigilmon — free tier, no credit card required.
Why ingress-nginx needs external monitoring
ingress-nginx manages traffic for your entire cluster from a single set of pods. That concentration of responsibility means a single misconfiguration or pod crash can silently take down every service it routes.
Internal health checks can't catch the full range of failures:
- Ingress pod crash with no restart — if the NGINX process dies and the pod fails to restart (OOMKilled, image pull error), Kubernetes marks the pod as
CrashLoopBackOffbut keeps reportingRunningbriefly — users see connection refused - ConfigMap reload failure — a bad annotation on any Ingress resource triggers a NGINX config reload failure; the controller logs an error but existing traffic may continue while new routes silently 404
- Admission webhook blocking valid Ingress resources — the ingress-nginx ValidatingWebhookConfiguration can reject Ingress updates even when NGINX itself is healthy
- Service weight drift — weighted canary routing via annotations can silently shift 100% of traffic to a broken backend after a botched deployment
- TLS certificate not picked up — cert-manager issues a new certificate, but ingress-nginx doesn't hot-reload it; users see expired cert errors that no internal probe detects
- Upstream connection pool exhaustion — NGINX worker processes hit file descriptor limits; upstream health probes pass but new requests time out
External monitoring from multiple geographic regions is the only way to catch all of these from the user's perspective.
What you'll need
- A Kubernetes cluster with ingress-nginx installed (
helm install ingress-nginx ingress-nginx/ingress-nginx) - At least one application exposed via an Ingress resource
- A free Vigilmon account
Step 1: Verify ingress-nginx is reachable
Check that the ingress-nginx controller has an external IP:
kubectl get svc -n ingress-nginx ingress-nginx-controller
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# ingress-nginx-controller LoadBalancer 10.96.200.10 203.0.113.10 80:30080/TCP,443:30443/TCP
If EXTERNAL-IP shows <pending>, your cloud provider hasn't assigned one yet. For bare-metal clusters, you need MetalLB or a NodePort service.
Get your cluster's external IP and confirm a backend application is reachable:
INGRESS_IP=$(kubectl get svc -n ingress-nginx ingress-nginx-controller \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl -H "Host: myapp.example.com" http://$INGRESS_IP/health
# {"status":"ok"}
Make sure your application has a /health or similar endpoint that returns HTTP 200.
Step 2: Monitor the ingress-nginx default backend
ingress-nginx ships with a default backend that responds to unmatched routes. Monitoring it gives you a fast signal that the controller itself is alive:
# The default backend serves a 404 for unknown hosts
curl -H "Host: nonexistent.example.com" http://$INGRESS_IP/
# 404 Not Found (this is expected and confirms the controller is running)
In Vigilmon:
- Log in and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- Set the URL to
http://<INGRESS_IP>/with a customHostheader for a known application - Set Expected status code to
200 - Set the check interval to 1 minute
- Save the monitor
Monitor the HTTPS endpoint too
If you use TLS (and you should), add a separate HTTPS monitor:
- Create a second monitor for
https://myapp.example.com/health - Enable SSL certificate expiry monitoring
- Set an alert threshold of 14 days before expiry
This catches cert-manager renewal failures before users see browser warnings.
Step 3: Monitor individual applications through ingress
Don't just monitor the ingress controller — monitor every critical application through its ingress route. This catches per-application issues like misconfigured path rewrites or broken upstream services:
# Example Ingress resource
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
namespace: production
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
Add a Vigilmon monitor for each application:
| Monitor URL | Expected Status | What it catches |
|-------------|-----------------|-----------------|
| https://myapp.example.com/health | 200 | App down, ingress misconfiguration, TLS failure |
| https://api.example.com/v1/status | 200 | API path rewrite failure |
| https://myapp.example.com | 200 | Root redirect, default backend routing |
Step 4: Monitor the metrics endpoint (optional)
ingress-nginx exposes Prometheus metrics on port 10254. If your cluster allows external access to this port, you can verify the metrics endpoint is alive:
kubectl port-forward -n ingress-nginx \
$(kubectl get pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx -o name | head -1) 10254:10254
curl http://localhost:10254/metrics | grep nginx_ingress_controller_requests
Key metrics to watch:
nginx_ingress_controller_requests— total request count by status codenginx_ingress_controller_ingress_upstream_latency_seconds— upstream response timenginx_ingress_controller_config_last_reload_successful— whether the last config reload succeeded (0 = broken config)
Step 5: Configure alert channels
When ingress-nginx goes down, every service behind it is affected. You want alerts the moment any of your ingress-monitored services fail.
Email alerts
- In Vigilmon, go to Alert Channels → Add Channel → Email
- Enter your on-call or infrastructure team email
- Assign the channel to all ingress monitors
Webhook (Slack / PagerDuty)
- Go to Alert Channels → Add Channel → Webhook
- Paste your Slack incoming webhook URL or PagerDuty integration URL
- Assign to all ingress monitors
Example Vigilmon alert payload:
{
"monitor_name": "myapp.example.com /health",
"status": "down",
"url": "https://myapp.example.com/health",
"started_at": "2025-03-15T14:30:00Z",
"duration_seconds": 60
}
Correlating Vigilmon alerts with ingress-nginx state
When an alert fires:
# 1. Check ingress-nginx controller pods
kubectl get pods -n ingress-nginx
# 2. Check recent events
kubectl describe pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx
# 3. Check NGINX config reload status
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=50 | grep -i "reload\|error\|warn"
# 4. Validate Ingress resources
kubectl get ingress -A
kubectl describe ingress myapp -n production
Step 6: Create a public status page
Group all your ingress-backed services on a public status page:
- In Vigilmon, go to Status Pages → New Status Page
- Name it "Production Services"
- Add all application monitors
- Group by function: API, Frontend, Admin, etc.
- Publish and share the URL with your team and customers
Monitoring summary
| Monitor | Type | What it catches |
|---------|------|-----------------|
| https://myapp.example.com/health | HTTP | Application down, ingress misconfiguration |
| https://api.example.com/v1/status | HTTP | API path routing failure |
| TLS cert for *.example.com | SSL expiry | cert-manager renewal failure |
| http://<INGRESS_IP>/ | HTTP | Controller pod crash, load balancer failure |
What's next
- Canary deployments — when using ingress-nginx canary annotations, add Vigilmon monitors for both stable and canary hostnames to catch weight drift
- Rate limiting validation — periodically test that your rate-limit annotations are active by monitoring for expected 429 responses from a separate monitor
- Multi-cluster — if you run multiple clusters, create one Vigilmon status page per cluster to track ingress health independently
Get started free at vigilmon.online — no credit card, monitors start running in under a minute.