In earlier part of our Kubernetes Ingress Playlist, we understood what Kubernetes Ingress is and why it's important in simplifying external access to services within your cluster.
In this part, we'll take a hands-on approach to install the NGINX Ingress Controller on an Amazon EKS cluster using Helm, and expose it to the internet using an AWS Network Load Balancer (NLB).
Why NGINX Ingress?
The NGINX Ingress Controller is one of the most widely adopted ingress controllers in the Kubernetes ecosystem. It's community-maintained and supports a wide range of use cases from basic path-based routing to advanced configurations like rate-limiting, TLS termination, and authentication.
Step 0: Default Behavior — Classic Load Balancer
By default, when you install the NGINX Ingress Controller with service.type=LoadBalancer, Kubernetes on AWS will provision a Classic Load Balancer (CLB).
helm upgrade -i ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.service.type=LoadBalancer
This may work for basic use cases, but for production workloads, it's recommended to use a Network Load Balancer (NLB) due to better performance, IP targeting, and TLS passthrough support.
Step 1: Install NGINX Ingress Controller with NLB
To create a Network Load Balancer when we create ingress, we use an annotation supported by AWS:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm upgrade -i ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.service.type=LoadBalancer \
--set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-type"="nlb"
Output will look like:
NAME: ingress-nginx
LAST DEPLOYED: Sun Aug 3 22:38:43 2025
NAMESPACE: ingress-nginx
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.
It may take a few minutes for the load balancer IP to be available.
You can watch the status by running 'kubectl get service --namespace ingress-nginx ingress-nginx-controller --output wide --watch'
This command installs the ingress controller in the ingress-nginx namespace and exposes it using a Kubernetes LoadBalancer type service and also adds an annotation that tells AWS to create a Network Load Balancer instead of a Classic Load Balancer
Once created, this NLB will automatically connect to the public subnets (if available) and expose an external IP.
$ kubectl get service --ns ingress-nginx ingress-nginx-controller
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 172.20.72.145 ad75493700bcd4c4db3470bdb08e9b2d-6739ce92cbd2f4d0.elb.us-east-1.amazonaws.com 80:30209/TCP,443:30549/TCP 4m33s
I own my domain and therefore I created an A record in route53 for the network load balancer created.
Step 2: Deploy a Simple Node.js App with a Service
Lets deploy our simple nodejs application using deployment and attach a service.
This creates a service named service-nodejs-app that forwards traffic on port 80 to port 8080 of the application pod. The selector matches pods with label app.kubernetes.io/name: nodejs-app.
Now the app is reachable within the cluster via this service.
---
apiVersion: v1
kind: Namespace
metadata:
name: simple-nodejs-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: simple-nodejs-app
name: deployment-nodejs-app
spec:
selector:
matchLabels:
app.kubernetes.io/name: nodejs-app
replicas: 5
template:
metadata:
labels:
app.kubernetes.io/name: nodejs-app
spec:
containers:
- image: public.ecr.aws/n4o6g6h8/simple-nodejs-app:latest
imagePullPolicy: Always
name: nodejs-app
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
namespace: simple-nodejs-app
name: service-nodejs-app
spec:
type: ClusterIP
ports:
- port: 80
name: http
targetPort: 8080
selector:
app.kubernetes.io/name: nodejs-app
You can see the deployment and service has been created
$ kubectl get all -n simple-nodejs-app
NAME READY STATUS RESTARTS AGE
pod/deployment-nodejs-app-55555bc798-6b9qt 1/1 Running 0 2m
pod/deployment-nodejs-app-55555bc798-b4gnl 1/1 Running 0 2m
pod/deployment-nodejs-app-55555bc798-gwrws 1/1 Running 0 2m
pod/deployment-nodejs-app-55555bc798-htfmk 1/1 Running 0 2m
pod/deployment-nodejs-app-55555bc798-p4pbf 1/1 Running 0 2m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/service-nodejs-app ClusterIP 172.20.41.147 <none> 80/TCP 2m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/deployment-nodejs-app 5/5 5 5 2m1s
NAME DESIRED CURRENT READY AGE
replicaset.apps/deployment-nodejs-app-55555bc798 5 5 5 2m1s
Step 3: Create Ingress Resource to Route Traffic
Create an Ingress resource to route incoming traffic from the NLB through the ingress controller to the internal Node.js service.
Here’s a sample ingress manifest:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-nodejs-ingress
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: chinmayto.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-nodejs-app
port:
number: 80
Replace with either the NLB DNS name (for testing) or your actual domain name (configured in Route 53), I have configured my own domain.
The annotation rewrite-target: / ensures that the request URI is rewritten properly when forwarding to the backend. This resource tells the ingress controller: "If someone hits / on this domain, forward traffic to simple-nodejs-service on port 80".
Apply and see nginx ingress created:
$ kubectl get ingress -n simple-nodejs-app
NAME CLASS HOSTS ADDRESS PORTS AGE
simple-nodejs-ingress nginx chinmayto.com 80 35s
Step 4: Access the App via External DNS
Test if traffic from the internet hits our NLB, goes through the ingress controller, and reaches the Node.js app.
Open a browser or use curl to access:
http://<HOST-NAME> # chinmay.to in our case
You should see the homepage or output of your Node.js app!
Conclusion
In this second part of our Kubernetes Ingress Playlist, you successfully took a practical step forward: installing the NGINX Ingress Controller on Amazon EKS. You learned how the controller works by default, typically provisioning a Classic Load Balancer, and how to switch to a Network Load Balancer for a production-grade, highly-performant ingress setup.
We also showed how to create an Ingress resource to route external traffic to a simple Node.js app, allowing you to see the end-to-end flow:
Client → NLB → NGINX Ingress → Kubernetes Service → Application Pod
This foundational pattern is a building block for modern microservices architectures on Kubernetes.
References
GitHub Repo: https://github.com/chinmayto/kubernetes-ingress-nginx
AWS Documentation: https://aws.amazon.com/blogs/containers/exposing-kubernetes-applications-part-3-nginx-ingress-controller/
Top comments (0)