Kubernetes Tutorial — Part 8: Ingress Controllers and HTTPS

By Suraj Ahir March 29, 2026 12 min read

← Part 7 Kubernetes Tutorial · Part 8 of 12 Part 9 →
Kubernetes Ingress Controller and HTTPS routing
Ingress routes external traffic to the right services based on host and path rules

In Part 4, we used LoadBalancer services to expose applications. Each LoadBalancer service provisions a separate cloud load balancer — which means a separate public IP address and a separate monthly bill. If you have ten microservices, that is ten load balancers. In AWS, each ELB costs money every hour, even when no traffic is flowing.

Ingress solves this. An Ingress is an HTTP routing layer that sits in front of all your services. It has one IP address, one load balancer, and routes traffic to different services based on the hostname and URL path in the request. Ten services can share one Ingress — one IP, one load balancer, one bill. And Ingress also handles HTTPS termination, so your services do not need to deal with TLS themselves.

This is the production-grade way to expose web applications in Kubernetes. Let us set it up properly.

Understanding the Ingress Architecture

There are two parts to Kubernetes Ingress. First, the Ingress resource — a YAML object that defines the routing rules. Second, the Ingress Controller — an actual pod running in your cluster that reads those rules and configures a real reverse proxy (usually nginx) to implement them.

Kubernetes does not include a built-in Ingress Controller. You must install one. The most widely used is the nginx-ingress-controller, maintained by the Kubernetes community. Other options include Traefik, Contour, and cloud-specific controllers like the AWS ALB Ingress Controller.

Installing nginx Ingress Controller on Minikube

Enable Ingress addon in Minikube
# Enable the nginx ingress addon (installs the controller automatically)
minikube addons enable ingress

# Verify the controller is running
kubectl get pods -n ingress-nginx

# Wait until it shows Running status
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=120s

Your First Ingress Resource

Let us create two deployments and route traffic to them based on URL paths.

Create two sample services
kubectl create deployment frontend --image=nginx
kubectl create deployment backend --image=httpd
kubectl expose deployment frontend --port=80
kubectl expose deployment backend --port=80
path-ingress.yaml — Route by URL path
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: backend
            port:
              number: 80
Apply and test
kubectl apply -f path-ingress.yaml

# Get the Minikube IP
minikube ip
# Add to /etc/hosts: 192.168.49.2 myapp.local

# Test routing
curl http://myapp.local/       # Goes to frontend (nginx)
curl http://myapp.local/api    # Goes to backend (httpd)

Host-Based Routing — Multiple Domains on One Ingress

multi-host-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-host-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: backend
            port:
              number: 80

HTTPS with cert-manager and Let's Encrypt

This is where Ingress really shines in production. cert-manager automatically provisions free TLS certificates from Let's Encrypt and renews them before they expire. You annotate your Ingress and cert-manager handles everything.

Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml

# Wait for cert-manager to be ready
kubectl get pods -n cert-manager
Create a ClusterIssuer for Let's Encrypt
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: your@email.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        ingress:
          class: nginx
Ingress with automatic HTTPS certificate
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - app.yourdomain.com
    secretName: app-tls-cert  # cert-manager creates this automatically
  rules:
  - host: app.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80

Once you apply this, cert-manager handles the Let's Encrypt ACME challenge, provisions the certificate, stores it in the specified Secret, and configures nginx to use it. Auto-renewal happens silently before expiry. No cron jobs, no manual steps — it just works.

Useful Ingress Annotations

The nginx Ingress Controller supports dozens of annotations to customise behaviour per Ingress resource.

Common useful annotations
metadata:
  annotations:
    # Redirect HTTP to HTTPS
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    # Set custom timeouts for long-running requests
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
    # Enable CORS
    nginx.ingress.kubernetes.io/enable-cors: "true"
    # Rate limiting
    nginx.ingress.kubernetes.io/limit-rps: "10"
    # Increase max upload size
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    # Whitelist specific IPs
    nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8"

Debugging Ingress Issues

Common Ingress debugging commands
# Check Ingress status
kubectl get ingress

# Describe for events and configuration
kubectl describe ingress my-ingress

# Check the nginx controller logs
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller

# Check the nginx configuration that was generated
kubectl exec -n ingress-nginx deployment/ingress-nginx-controller -- nginx -T | grep -A 20 "server_name myapp"

What is Next

Traffic is now flowing into your cluster properly, with HTTPS and intelligent routing. The next challenge is reliability. What happens when a pod is still starting but Kubernetes is already routing traffic to it? What if a pod needs 30 seconds to initialise before it can handle requests? In Part 9, we cover Health Checks and Probes — the mechanism that tells Kubernetes exactly when a pod is truly ready to receive traffic and when it is unhealthy and should be restarted.

Frequently Asked Questions

What is the difference between a Service and an Ingress?

A Service exposes pods at the TCP level. An Ingress is an HTTP routing layer sitting in front of multiple Services, routing by hostname and URL path. One Ingress replaces many LoadBalancer services, saving cost and complexity.

What is an Ingress Controller?

An Ingress Controller is the actual pod that implements Ingress rules by configuring a real reverse proxy (nginx, Traefik, etc.). Kubernetes defines the Ingress API but does not include a controller — you install one separately. Minikube includes nginx as an addon.

How do I get free HTTPS in Kubernetes?

Install cert-manager, create a ClusterIssuer for Let's Encrypt, and annotate your Ingress with cert-manager.io/cluster-issuer. cert-manager automates certificate issuance and renewal completely — free and hands-off.

Can one Ingress handle multiple domains?

Yes. Define multiple host rules in one Ingress resource. Each host can have independent path routing and its own TLS certificate. This is far more cost-efficient than one LoadBalancer per service.

What is path-based routing?

Routing traffic to different services based on the URL path. Example: /api goes to the API service, /uploads goes to a file service, everything else to the frontend. All under one hostname and one IP address.

Key takeaways

Continue reading
Part 9 — Autoscaling and Resources
Scale up. Scale down. Sleep peacefully.
Suraj Ahir — author of SRJahir Tech

Written by

Suraj Ahir

Cloud & DevOps engineer running four live production services on my own AWS infrastructure. I write everything on this site myself — no ghostwriters, no AI filler.

← Part 7 Kubernetes Tutorial · Part 8 of 12 Part 9 →
← Back to Blog
Disclaimer: This content is for educational purposes only.