Skip to main content
The Nginx Ingress Controller manages external access to services in a Kubernetes cluster, typically HTTP/HTTPS. It provides features like SSL termination, name-based virtual hosting, and path-based routing.

Installation

1

Clone Repository

Clone the official NGINX Ingress Controller repository:
git clone https://github.com/nginxinc/kubernetes-ingress.git --branch v3.1.1
cd kubernetes-ingress/deployments/
2

Create Namespace and Service Account

kubectl apply -f common/ns-and-sa.yaml
3

Apply RBAC Resources

kubectl apply -f rbac/rbac.yaml
kubectl apply -f rbac/ap-rbac.yaml
kubectl apply -f rbac/apdos-rbac.yaml
4

Create Configuration Resources

kubectl apply -f common/nginx-config.yaml
kubectl apply -f common/ingress-class.yaml
5

Install Custom Resource Definitions

kubectl apply -f common/crds/k8s.nginx.org_virtualservers.yaml
kubectl apply -f common/crds/k8s.nginx.org_virtualserverroutes.yaml
kubectl apply -f common/crds/k8s.nginx.org_transportservers.yaml
kubectl apply -f common/crds/k8s.nginx.org_policies.yaml
kubectl apply -f common/crds/k8s.nginx.org_globalconfigurations.yaml
6

Deploy Ingress Controller

Deploy as DaemonSet to run on all nodes:
kubectl apply -f daemon-set/nginx-ingress.yaml
7

Verify Installation

kubectl get ns
kubectl -n nginx-ingress get ds
kubectl -n nginx-ingress get pods
After installation, accessing any node IP via a browser will show a 404 Not Found error from NGINX. This confirms the controller is receiving requests and waiting for Ingress resources.

Deploy Sample Application

1

Create Application Manifest

petclinic.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: petclinic
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: petclinic
  namespace: petclinic
spec:
  replicas: 2
  selector:
    matchLabels:
      app: petclinic
  template:
    metadata:
      labels:
        app: petclinic
    spec:
      containers:
      - name: pet
        image: lovelearnlinux/webserver:v1
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: petclinic-svc
  namespace: petclinic
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: petclinic
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: petclinic-ingress
  namespace: petclinic
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: petclinic.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: petclinic-svc
            port:
              number: 80
2

Apply the Manifest

kubectl apply -f petclinic.yaml

Testing the Ingress

Local DNS Configuration

Since we don’t have a real DNS setup, modify /etc/hosts on worker nodes:
# Add this line to /etc/hosts on each worker node
<worker-ip> petclinic.example.com

Test Access

# From the worker node
curl http://petclinic.example.com
You should see the application response.

Ingress Configuration

Path-Based Routing

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: path-based-ingress
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
      - path: /web
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

Host-Based Routing

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: host-based-ingress
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: app1.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app1-service
            port:
              number: 80
  - host: app2.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app2-service
            port:
              number: 80

SSL/TLS Configuration

1

Create TLS Secret

kubectl create secret tls my-tls-secret \
  --cert=path/to/tls.crt \
  --key=path/to/tls.key \
  -n petclinic
2

Update Ingress with TLS

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: petclinic-ingress
  namespace: petclinic
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - petclinic.example.com
    secretName: my-tls-secret
  rules:
  - host: petclinic.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: petclinic-svc
            port:
              number: 80

Common Annotations

annotations:
  nginx.ingress.kubernetes.io/rewrite-target: /

Advanced Features

Basic Authentication

1

Create htpasswd File

htpasswd -c auth username
2

Create Secret

kubectl create secret generic basic-auth --from-file=auth -n petclinic
3

Add Annotation

annotations:
  nginx.ingress.kubernetes.io/auth-type: basic
  nginx.ingress.kubernetes.io/auth-secret: basic-auth
  nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'

Custom Error Pages

annotations:
  nginx.ingress.kubernetes.io/custom-http-errors: "404,503"
  nginx.ingress.kubernetes.io/default-backend: custom-error-pages

Canary Deployments

# Production Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production
  annotations:
    nginx.ingress.kubernetes.io/canary: "false"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - backend:
          service:
            name: production-service
            port:
              number: 80
        path: /
        pathType: Prefix
---
# Canary Ingress (10% traffic)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - backend:
          service:
            name: canary-service
            port:
              number: 80
        path: /
        pathType: Prefix

Monitoring and Troubleshooting

View Ingress Controller Logs

kubectl logs -n nginx-ingress <pod-name>

Check Ingress Status

kubectl get ingress -A
kubectl describe ingress <ingress-name> -n <namespace>

Test Backend Connectivity

kubectl run test --image=curlimages/curl -it --rm -- sh
curl http://<service-name>.<namespace>.svc.cluster.local

Deployment Options

Runs an Ingress Controller pod on every node. Suitable for:
  • High availability
  • Direct node port access
  • On-premises clusters
kubectl apply -f daemon-set/nginx-ingress.yaml
Runs a specific number of replicas. Suitable for:
  • Cloud environments with load balancers
  • Resource-constrained clusters
  • Better control over pod placement
kubectl apply -f deployment/nginx-ingress.yaml
The DaemonSet approach ensures that every node can handle incoming traffic directly, while the Deployment approach works better with cloud load balancers.

Best Practices

  • Use IngressClass to support multiple ingress controllers
  • Always set resource limits for the ingress controller pods
  • Enable RBAC and use least privilege principles
  • Implement TLS for all public-facing applications
  • Monitor ingress controller metrics and logs
  • Use separate ingress resources per application/namespace
  • Test configuration changes in non-production first
Direct exposure of node ports in production is not recommended. Use a proper LoadBalancer service or cloud provider’s load balancer integration.

Build docs developers (and LLMs) love