Nginx Ingress Controller
Luke Hebert
- 7 minutes read - 1315 wordsThe Ingress-Nginx Controller is a purpose-built nginx for routing traffic in Kubernetes. It reads its configuration from the nginx-configuration ConfigMap, environment variables, and matchLabel selectors. Expose it externally via a Service of type NodePort or LoadBalancer, and grant it RBAC roles so it can watch cluster resources and update its configuration automatically.
Quick start
Install the controller using one of the following methods:
- Helm (using the project repository chart)
kubectl applywith YAML manifests- Platform-specific addons (minikube, MicroK8s)
On most clusters it works out of the box. For performance tuning or better logging, see the environment-specific instructions below.
Helm install
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespaceThis installs (or upgrades, if already present) the controller in the ingress-nginx namespace. To see all configurable values:
helm show values ingress-nginx --repo https://kubernetes.github.io/ingress-nginxCloud-provider annotations (AWS/GCP/Azure)
The default Helm values are generic. Cloud-provider-specific annotations must be set by you. For example, on AWS with an NLB (--type LoadBalancer), the healthcheck annotations below are required for target-type IP:
annotations:
service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: deregistration_delay.timeout_seconds=270
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: /healthz
service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "10254"
service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: http
service.beta.kubernetes.io/aws-load-balancer-healthcheck-success-codes: 200-299
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-type: nlb
service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules: "true"
service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-security-groups: "sg-something1 sg-something2"
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: "somebucket"
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: "ingress-nginx"
service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "5"See AWS LB Controller for the full list.
kubectl install
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/cloud/deploy.yamlThis manifest was generated with helm template, so the resulting resources are nearly identical to a Helm install.
Running Kubernetes 1.18 or earlier? See Running on Kubernetes versions older than 1.19. API deprecations mean the default manifest may not work on older clusters.
Firewall configuration
Check open ports with kubectl -n ingress-nginx get pod -o yaml. You generally need:
- 8443 open between all Kubernetes nodes (admission controller)
- 80 (HTTP) and/or 443 (HTTPS) open to the public on the nodes your DNS points to
Pre-flight check
kubectl get pods --namespace=ingress-nginxWait for the controller pod to be ready:
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=120sLocal testing
Create a demo web server and service:
kubectl create deployment demo --image=httpd --port=80
kubectl expose deployment demoCreate an Ingress resource (using a host that maps to localhost):
kubectl create ingress demo-localhost --class=nginx \
--rule="demo.localdev.me/*=demo:80"Forward a local port to the controller:
kubectl port-forward --namespace=ingress-nginx service/ingress-nginx-controller 8080:80Port-forwarding maps local port 8080 to the controller’s service port 80, simulating external traffic. It is a quick demo tool, not a production pattern.
Test with curl:
curl --resolve demo.localdev.me:8080:127.0.0.1 http://demo.localdev.me:8080You should see an HTML response containing “It works!”.
Online testing
If your cluster supports LoadBalancer services, an external IP or FQDN will be assigned:
kubectl get service ingress-nginx-controller --namespace=ingress-nginxIf EXTERNAL-IP shows <pending>, your cluster cannot provision a load balancer. Once you have the IP/FQDN, point a DNS record at it, then create an Ingress:
kubectl create ingress demo --class=nginx \
--rule="www.demo.io/*=demo:80"Visit http://www.demo.io/ and you should see the “It works!” page. You are now serving a public website from Kubernetes. 🎉
Environment-specific instructions
Local development clusters
minikube
minikube addons enable ingressMicroK8s
microk8s enable ingressSee the MicroK8s documentation for details.
Docker Desktop
Kubernetes is available in Docker Desktop (Mac and Windows, from version 18.06.0-ce). Enable Kubernetes in Docker settings; kubectl get nodes should show a single node called docker-desktop.
Install using the Helm or kubectl commands above. On most systems the controller gets EXTERNAL-IP of localhost, making it reachable on localhost:80. If that does not work, fall back to the kubectl port-forward method in the local testing section.
Rancher Desktop
Rancher Desktop uses K3s under the hood, which defaults to Traefik as the ingress controller. To use Ingress-Nginx instead, disable Traefik from Preferences > Kubernetes, then install using the quick start commands above. Follow the local testing section to verify.
Cloud deployments
Two common optimizations for cloud load balancers:
externalTrafficPolicy: Local- saves a hop when the LB does active health checks. With Helm:--set controller.service.externalTrafficPolicy=Local.- PROXY protocol - lets the controller see real client IPs. Enable in both the controller (
--set controller.config.use-proxy-protocol=true) and the cloud LB configuration.
The sections below provide manifests with these options enabled where applicable.
AWS
Uses a Network Load Balancer (NLB) behind a Type=LoadBalancer Service.
The templates below use the legacy in-tree LB. For EKS, see AWS documentation on using the AWS Load Balancer Controller.
NLB:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/aws/deploy.yamlTLS termination at the NLB:
By default TLS terminates in the controller. To terminate at the NLB instead:
Download the template:
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yamlSet your VPC CIDR:
proxy-real-ip-cidr: XXX.XXX.XXX/XXSet your ACM certificate ARN:
arn:aws:acm:us-west-2:XXXXXXXX:certificate/XXXXXX-XXXXXXX-XXXXXXX-XXXXXXXXDeploy:
kubectl apply -f deploy.yaml
NLB idle timeouts:
The default TCP idle timeout is 350 s (range 60-6000 s). Ensure keepalive_timeout (default 75 s) is set below your idle timeout. See the official AWS documentation for details.
GCE-GKE
The default GKE LoadBalancer does not support the PROXY protocol. use-proxy-protocol will not work with it.
Grant cluster-admin (if not already present):
kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole cluster-admin \
--user $(gcloud config get-value account)Install:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/cloud/deploy.yamlFor private clusters, add a firewall rule allowing master nodes to reach port 8443/tcp on worker nodes (or extend the existing 80/443/10254 rule). See the Official GCP Documentation.
See the GKE documentation on adding rules and the related Kubernetes issue for more detail. Proxy protocol is supported in GCE; see the official documentation to enable it.
Azure
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/cloud/deploy.yamlSee the official AKS documentation for Azure-specific annotations.
Digital Ocean
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/do/deploy.yamlThe default manifest sets only service.beta.kubernetes.io/do-loadbalancer-enable-proxy-protocol: "true". This is functional, but DO LoadBalancer graphs will show no data without additional annotations. Some of those annotations require user-specific values, so they are not included in the default install. See the linked issue for the full list and guidance.
Scaleway
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/scw/deploy.yamlSee the Scaleway documentation tutorial for proxy-protocol configuration.
Exoscale
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/exoscale/deploy.yamlSee the Exoscale Cloud Controller Manager documentation for supported annotations.
Oracle Cloud Infrastructure
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/cloud/deploy.yamlSee the OCI Cloud Controller Manager documentation for available annotations.
OVHcloud
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm -n ingress-nginx install ingress-nginx ingress-nginx/ingress-nginx --create-namespaceSee the complete tutorial for details.
Bare-metal clusters
Applies to bare-metal servers and “raw” VMs with manually installed Kubernetes (CentOS, Ubuntu, etc.).
For quick testing, use a NodePort (typically in the 30000-32767 range):
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/baremetal/deploy.yamlFor port 80 instead of a random NodePort, see bare-metal considerations.
Miscellaneous
Checking controller version
POD_NAMESPACE=ingress-nginx
POD_NAME=$(kubectl get pods -n $POD_NAMESPACE -l app.kubernetes.io/name=ingress-nginx --field-selector=status.phase=Running -o name)
kubectl exec $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --versionScope
By default the controller watches Ingress objects in all namespaces. Limit it to one namespace with --watch-namespace or the Helm value controller.scope. Note: the default-ssl-certificate secret must exist in the watched namespace.
See “How to install multiple Ingress controllers in the same cluster” for details.
Webhook network access
The admission webhook validates Ingress definitions. Ensure no NetworkPolicies or firewalls block the API server from reaching the ingress-nginx-controller-admission service.
Certificate generation
On first start, two Jobs generate the SSL certificate for the admission webhook. This can add up to two minutes before Ingress resources can be created and validated.
Wait for readiness:
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=120sRunning on Kubernetes versions older than 1.19
Ingress API versions over time:
| Kubernetes version | Supported Ingress API |
|---|---|
| < 1.19 | v1beta1 only |
| 1.19 - 1.21 | v1beta1 and v1 |
| ≥ 1.22 | v1 only |
Ingress-Nginx Controller support:
| Controller version | Supported Ingress API |
|---|---|
| < 1.0 | v1beta1 only |
| ≥ 1.0 | v1 only |
So: Kubernetes 1.19+ pairs with the latest controller. Kubernetes 1.18 or earlier requires controller 0.X (e.g. 0.49) and Helm chart 3.X (add --version='<4' to the helm install command).