Deploying NGINX Ingress with HTTPS on Kubernetes: The Complete Guide

Complete step-by-step guide to deploy NGINX Ingress Controller with Let's Encrypt TLS certificates on Kubernetes using Helm, kubectl & cert-manager

Quick Navigation

Difficulty: 🟡 Intermediate
Estimated Time: 30-45 minutes
Prerequisites: Basic Kubernetes knowledge, kubectl experience, Helm familiarity, Understanding of Ingress resources

What You'll Learn

This tutorial covers essential Kubernetes NGINX Ingress concepts and tools:

  • NGINX Ingress Controller - Installation and configuration with Helm
  • TLS Certificate Management - Let's Encrypt integration with cert-manager
  • Ingress Resource Configuration - HTTP and HTTPS routing setup
  • Security Best Practices - Production-ready HTTPS configuration
  • Testing and Validation - Verification of secure connections
  • Troubleshooting - Common issues and debugging techniques

Prerequisites

  • Basic Kubernetes knowledge and cluster administration experience
  • kubectl command-line tool experience
  • Helm package manager familiarity
  • Understanding of Ingress resources and networking concepts

Introduction

Kubernetes Ingress is the gateway to your services, enabling controlled access from the outside world. When combined with NGINX Ingress Controller and Let's Encrypt TLS certificates, it becomes a secure, production-ready solution.

This comprehensive guide walks you through installing the NGINX Ingress Controller, setting up a sample NGINX backend, exposing it to the internet, adding HTTPS using Let's Encrypt, and testing with curl.

Whether you're on cloud, Minikube, or bare-metal, this tutorial has got you covered!

Step-by-Step Installation

Step 1: Install the NGINX Ingress Controller

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

Verify Installation

kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx

Step 2: Deploy a Sample NGINX Backend (Optional for Testing)

kubectl create deployment nginx-backend --image=nginx
kubectl expose deployment nginx-backend --port=80 --target-port=80 --name=nginx-backend

Step 3: Create a Basic Ingress Resource

Create a file named nginx-ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-backend-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: hello-world.example # replace with actual domain
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-backend
            port:
              number: 80

Apply the Ingress

kubectl apply -f nginx-ingress.yaml

Step 4: Expose It to the World (For Single Node Deployment)

Change ingress type from LoadBalancer to NodePort:

kubectl edit svc -n ingress-nginx

Get Your External IP

Create a script to fetch the service URL:

#!/bin/bash

# Set namespace and service variables
NAMESPACE="ingress-nginx"
SERVICE="ingress-nginx-controller"

# Fetch the NodePort and handle any potential errors
NODE_PORT=$(kubectl get --namespace $NAMESPACE -o jsonpath="{.spec.ports[0].nodePort}" services $SERVICE 2>/dev/null) 
if [ -z "$NODE_PORT" ]; then
  echo "Error: Unable to fetch the NodePort for service '$SERVICE' in namespace '$NAMESPACE'."
  exit 1
fi

# Fetch the Node IP and handle any potential errors
NODE_IP=$(kubectl get nodes --namespace $NAMESPACE -o jsonpath="{.items[0].status.addresses[0].address}" 2>/dev/null) 
if [ -z "$NODE_IP" ]; then
  echo "Error: Unable to fetch the Node IP in namespace '$NAMESPACE'."
  exit 1
fi

# Display the URL                                                                                                     
URL="http://$NODE_IP:$NODE_PORT"                                                                                      
echo "Service URL: $URL"

Example Output: Service URL: http://10.9.15.138:30810

Step 5: Test with cURL

curl -H "Host: hello-world.example" http://10.9.15.138:30810

You should see the default NGINX Welcome Page.

Step 6: Enable HTTPS with Let's Encrypt & cert-manager

Install cert-manager

helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set installCRDs=true

Verify cert-manager Installation

kubectl get pods -n cert-manager

Step 7: Create a ClusterIssuer (Staging)

Create a file named cluster-issuer-staging.yaml:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    email: your-email@example.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-staging-key
    solvers:
    - http01:
        ingress:
          class: nginx

Apply the ClusterIssuer:

kubectl apply -f cluster-issuer-staging.yaml

Step 8: Update Your Ingress for HTTPS

Create a file named nginx-ingress-tls.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-backend-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-staging
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - hello-world.example # replace with actual domain
    secretName: nginx-backend-tls
  rules:
  - host: hello-world.example   # replace with actual domain
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-backend
            port:
              number: 80

Apply the updated Ingress:

kubectl apply -f nginx-ingress-tls.yaml

Check Certificate Status

kubectl describe certificate -A

Expected Output:

Name:         nginx-backend-tls
Namespace:    fast-api-app
Labels:       <none>
Annotations:  <none>
API Version:  cert-manager.io/v1
Kind:         Certificate
Metadata:
  Creation Timestamp:  2025-05-20T19:40:37Z
  Generation:          1
  Owner References:
    API Version:           networking.k8s.io/v1
    Block Owner Deletion:  true
    Controller:            true
    Kind:                  Ingress

Step 9: Test HTTPS with cURL

curl -k -H "Host: hello-world.example" https://10.9.15.138:32243

Use curl https://$HOST (without -k) after switching to production issuer.

Step 10: Switch to Let's Encrypt Production

Create a file named cluster-issuer-prod.yaml:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: your-email@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        ingress:
          class: nginx

Apply the production ClusterIssuer:

kubectl apply -f cluster-issuer-prod.yaml

Update Your Ingress Annotation

Change the annotation in your Ingress to:

cert-manager.io/cluster-issuer: letsencrypt-prod

Re-apply to enable real TLS.

Conclusion

You're now running a fully secure, internet-accessible, NGINX-backed service on Kubernetes using Ingress + Let's Encrypt!

Whether it's for dev or prod, this setup gives you a rock-solid foundation.

Key Takeaways

  • Secure Gateway - NGINX Ingress Controller with TLS encryption
  • Automated Certificates - Let's Encrypt integration with cert-manager
  • Production Ready - Staging to production certificate workflow
  • Easy Testing - Simple verification with curl commands
  • Flexible Configuration - Works across different cluster types

Next Steps

  1. Test your HTTPS setup with the provided commands
  2. Configure custom domains for your services
  3. Implement security headers and additional security measures
  4. Set up monitoring for your ingress traffic
  5. Explore advanced features like rate limiting and authentication

Troubleshooting

Common Issues

  1. Certificate Not Issued: Check cert-manager logs and ClusterIssuer status
  2. Ingress Not Working: Verify NGINX Ingress Controller pods are running
  3. TLS Handshake Failures: Ensure proper hostname resolution and certificate validation

Debug Commands

# Check cert-manager status
kubectl get pods -n cert-manager
kubectl logs -n cert-manager deployment/cert-manager

# Check Ingress status
kubectl describe ingress nginx-backend-ingress
kubectl get events --sort-by='.lastTimestamp'

# Verify TLS secret
kubectl get secret nginx-backend-tls -o yaml

Next Steps

  • Production Hardening: Configure resource limits and security policies
  • Monitoring: Set up Prometheus and Grafana for Ingress metrics
  • Automation: Create Helm charts for repeatable deployments
  • Advanced Config: Explore NGINX Ingress annotations and custom configurations

External Resources


Tags: #Kubernetes #NGINXIngress #LetsEncrypt #certmanager #CloudNative #DevOps #Minikube #K3s #KubernetesIngress #TLS #HTTPS #nipio #k8s #Helm #Networking