Overview#

The NGINX Ingress Controller (open source version) is a widely used Kubernetes ingress controller that leverages NGINX as a reverse proxy and load balancer.

This guide walks you through installing it with Helm, following best practices and referencing the official documentation.
Use the latest stable release (e.g., 2.3.1 at the time of writing).


Prerequisites#

  • A running Kubernetes cluster (v1.22+ recommended)
  • Helm 3 installed (≤ 3.18.4 — avoid 3.18.5 due to an OCI bug)
  • kubectl configured for your cluster
  • Default setting: controller.nginxplus=false (open source)
  • CRDs enabled: controller.enableCustomResources=true (default)

💡 Notes

  • This covers the open-source version (not NGINX Plus).
  • Deploy into a dedicated namespace like nginx-ingress.
  • The controller runs as a Deployment (1 replica) and exposes a LoadBalancer service.

Step 1: Create Namespace#

kubectl create namespace nginx-ingress

Step 2: Install the Helm Chart#

The chart is distributed via the OCI registry, so there’s no need to add a repo.

helm install my-nginx-ingress oci://ghcr.io/nginx/charts/nginx-ingress \
  --namespace nginx-ingress \
  --version 2.3.1  # Replace with the latest stable version
  • Installs CRDs automatically (omit with --skip-crds if managing manually)
  • Customize release name as needed

Manual CRD management example:

helm pull oci://ghcr.io/nginx/charts/nginx-ingress --untar --version 2.3.1
cd nginx-ingress
helm install my-nginx-ingress . --namespace nginx-ingress

Edge build (testing only):

helm install my-nginx-ingress oci://ghcr.io/nginx/charts/nginx-ingress \
  --version 0.0.0-edge \
  --namespace nginx-ingress

Step 3: Verify Installation#

1. Check pods

kubectl get pods -n nginx-ingress

If pods aren’t ready, ensure CRDs exist:

kubectl get crd | grep ingress

2. Check Service

kubectl get svc -n nginx-ingress

Retrieve external IP for LoadBalancer type:

kubectl get svc my-nginx-ingress-nginx-ingress-controller \
  -n nginx-ingress \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

For NodePort or ClusterIP, modify with:

--set controller.service.type=NodePort

3. Health check (optional)

curl http://<EXTERNAL-IP>/nginx-health

Expected result: HTTP 200.


Common Customizations#

Use --set flags or a values.yaml file to override defaults.

Watch specific namespaces

--set controller.watchNamespace="default\,nginx-ingress"

Set as default IngressClass

--set controller.ingressClass.setAsDefaultIngress=true

Adjust NGINX log level

--set controller.config.entries.error-log-level=warn

Configure default TLS

--set controller.defaultTLS.secret=nginx-ingress/tls-secret

Or inline (base64):

--set controller.defaultTLS.cert=<base64-cert> \
--set controller.defaultTLS.key=<base64-key>

Resource limits

--set controller.resources.requests.cpu=200m \
--set controller.resources.requests.memory=256Mi

Namespace-scoped RBAC

--set rbac.clusterrole.create=false

To explore all chart parameters:

helm show values oci://ghcr.io/nginx/charts/nginx-ingress --version 2.3.1

Or see NGINX Helm parameters.


Post-Installation#

1. Create an Ingress resource

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

Apply with:

kubectl apply -f ingress.yaml

2. Enable metrics Prometheus metrics are exposed on port 9113 by default.

3. Upgrade when new version is released

helm upgrade my-nginx-ingress oci://ghcr.io/nginx/charts/nginx-ingress \
  --version <new-version> \
  --namespace nginx-ingress

⚠️ Avoid toggling controller.ingressClass.create during upgrades to prevent accidental resource deletion.


Troubleshooting#

Issue Likely Cause Fix
Pods not Ready Missing CRDs Reinstall without --skip-crds
Multiple Instances CRD version mismatch Multi-controller setup
No TLS Secret HTTPS fails Provide default TLS secret
General Debug View logs kubectl logs -n nginx-ingress deployment/my-nginx-ingress-nginx-ingress-controller