How to Set Up Kubernetes Ingress with Nginx Ingress Controller on RHEL 7
Kubernetes Ingress provides a powerful way to expose HTTP and HTTPS routes from outside the cluster to services running inside it. Rather than creating a separate LoadBalancer service for every application, an Ingress resource lets you define routing rules in one place, directing traffic based on hostnames and URL paths. The Nginx Ingress Controller is the most widely used implementation, acting as a reverse proxy that watches Ingress resources and dynamically reconfigures Nginx to match. On Red Hat Enterprise Linux 7, where stability and enterprise-grade configurations are paramount, setting up Ingress correctly ensures your applications are accessible, secure, and maintainable at scale. This tutorial walks you through deploying the Nginx Ingress Controller, defining Ingress rules, enabling TLS, and testing your setup end to end.
Prerequisites
- RHEL 7 nodes with a running Kubernetes cluster (kubeadm or existing)
kubectlconfigured and pointing to your cluster- Helm 3 installed, or access to apply raw manifests with
kubectl apply - A running application deployed as a Kubernetes Service (ClusterIP is sufficient)
- DNS or
/etc/hostsentries for your test hostnames - Root or sudo access on the RHEL 7 control plane node
Step 1: Install the Nginx Ingress Controller
You can deploy the Nginx Ingress Controller using either Helm or a raw manifest. Both methods are covered below. Using Helm is recommended for production as it simplifies upgrades and configuration management.
Option A: Install with Helm
If Helm is not yet installed, fetch and install it first:
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
Add the ingress-nginx Helm repository and install the controller into its own namespace:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx
--namespace ingress-nginx
--create-namespace
--set controller.service.type=NodePort
--set controller.service.nodePorts.http=30080
--set controller.service.nodePorts.https=30443
On bare-metal RHEL clusters without a cloud load balancer, NodePort is the practical choice. Adjust port numbers as needed for your firewall rules.
Option B: Install with kubectl apply
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.4/deploy/static/provider/baremetal/deploy.yaml
Verify the controller pod is running:
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
Wait until the ingress-nginx-controller pod reaches Running status before proceeding.
Step 2: Deploy a Sample Application
If you do not already have a test application, deploy one now so you have something to route traffic to:
kubectl create deployment webapp --image=nginx:stable --port=80
kubectl expose deployment webapp --type=ClusterIP --port=80 --name=webapp-svc
Confirm the service is created:
kubectl get svc webapp-svc
Step 3: Create an Ingress Resource
An Ingress resource defines how external traffic reaches your service. Save the following YAML to webapp-ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: webapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp-svc
port:
number: 80
Apply the resource:
kubectl apply -f webapp-ingress.yaml
kubectl get ingress webapp-ingress
The rewrite-target: / annotation tells Nginx to strip any prefix from the upstream request. If you route /app to a backend that expects requests at /, this annotation is essential.
Step 4: Configure TLS with a Kubernetes Secret
To serve traffic over HTTPS, you need a TLS certificate stored as a Kubernetes Secret. For testing, generate a self-signed certificate:
openssl req -x509 -nodes -days 365 -newkey rsa:2048
-keyout webapp.key
-out webapp.crt
-subj "/CN=webapp.example.com/O=webapp"
Create the TLS Secret:
kubectl create secret tls webapp-tls
--key webapp.key
--cert webapp.crt
Update your Ingress to reference the secret and add SSL redirect annotations:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- hosts:
- webapp.example.com
secretName: webapp-tls
rules:
- host: webapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp-svc
port:
number: 80
kubectl apply -f webapp-ingress.yaml
With ssl-redirect: "true", any HTTP request to webapp.example.com is automatically redirected to HTTPS.
Step 5: Test with /etc/hosts and curl
If you do not have DNS configured, add an entry to /etc/hosts on your RHEL 7 machine. First, identify a node IP:
kubectl get nodes -o wide
Then edit your hosts file:
echo "192.168.1.100 webapp.example.com" >> /etc/hosts
Test HTTP, which should redirect to HTTPS:
curl -v http://webapp.example.com:30080/
Test HTTPS using the -k flag to skip certificate verification for self-signed certs:
curl -k -v https://webapp.example.com:30443/
A successful response will show the Nginx welcome page HTML and a 200 OK status code. If you see a 404 from the Nginx Ingress Controller itself rather than your app, double-check that the host field in your Ingress matches your request hostname exactly.
Step 6: Path-Based Routing for Multiple Services
One of the primary benefits of Ingress is routing multiple services under a single hostname using different URL paths:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-path-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: webapp.example.com
http:
paths:
- path: /app1(/|$)(.*)
pathType: Prefix
backend:
service:
name: app1-svc
port:
number: 80
- path: /app2(/|$)(.*)
pathType: Prefix
backend:
service:
name: app2-svc
port:
number: 80
The regex capture group $2 in rewrite-target ensures that /app1/login is passed to the backend as /login rather than the full path with the prefix intact.
Step 7: Open Firewall Ports on RHEL 7
On RHEL 7, firewalld manages firewall rules. Open the NodePort ports so external clients can reach the Ingress Controller:
firewall-cmd --permanent --add-port=30080/tcp
firewall-cmd --permanent --add-port=30443/tcp
firewall-cmd --reload
firewall-cmd --list-ports
If SELinux is in enforcing mode, verify that the Nginx process inside the container has permission to bind to these ports. In most cases the container runtime handles this transparently, but you may need to check audit logs with ausearch -m avc -ts recent if connections are refused unexpectedly despite open ports.
Conclusion
You have successfully deployed the Nginx Ingress Controller on a Kubernetes cluster running on RHEL 7, defined Ingress routing rules with host-based and path-based matching, secured traffic with TLS using a Kubernetes Secret, added annotations for automatic SSL redirect, and verified connectivity using curl and /etc/hosts entries. The Nginx Ingress Controller is highly configurable through annotations, allowing you to control rate limiting, basic authentication, upstream connection timeouts, CORS headers, and custom error pages without modifying your application code. In production, replace the self-signed certificate with one issued by Let’s Encrypt using cert-manager, and consider using MetalLB as a load balancer instead of NodePort for a more seamless external IP experience on bare-metal RHEL infrastructure.