⚡ ~/naveed/lab DevOps Lab
Home > DevOps Lab > Tutorials & Runbooks > Kubernetes Ingress-NGINX & Cert-Manager SSL
Kubernetes Ingress Let's Encrypt TLS Cert-Manager v1.15+

Kubernetes Ingress-NGINX & Cert-Manager Automated SSL: Step-by-Step Production Runbook

1. The Architecture: How Automated ACME Zero-Touch SSL Works

Managing SSL/TLS certificates manually across dozens of microservices leads to expired certificates, browser security warnings, and costly production downtime. By pairing Ingress-NGINX with Cert-Manager, Kubernetes automatically communicates with Let's Encrypt using the ACME protocol, verifies domain ownership via an HTTP-01 challenge, provisions an X.509 certificate, stores it safely in a Kubernetes Secret, and renews it automatically 30 days before expiration.

// Automated ACME HTTP-01 Certificate Lifecycle
Step 1 • Ingress Annotation
ClusterIssuer Trigger

Ingress manifest annotated with cluster-issuer: letsencrypt-prod.

Step 2 • ACME Challenge
HTTP-01 Solver Pod

Temporary pod created to serve validation token at /.well-known/acme-challenge/.

Step 3 • Validation
Let's Encrypt Verification

ACME servers query public port 80 to verify domain DNS ownership.

Step 4 • Dynamic Secret
Zero-Downtime TLS

X.509 cert stored in K8s Secret; Ingress-NGINX hot-reloads TLS instantly.

2. Step 1: Install Ingress-NGINX Controller

Deploy the official Ingress-NGINX controller using Helm:

bash — install ingress-nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

# Install Ingress-NGINX
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace \
  --set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-type"="nlb" \
  --wait

# Verify external IP / LoadBalancer hostname
kubectl get svc -n ingress-nginx ingress-nginx-controller

3. Step 2: Install Jetstack Cert-Manager with CRDs

Install Cert-Manager into the cert-manager namespace with Custom Resource Definitions enabled:

bash — install cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update

helm upgrade --install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.15.3 \
  --set crds.enabled=true \
  --wait

# Verify pods running
kubectl get pods -n cert-manager

4. Step 3: Configure Let's Encrypt ClusterIssuer

A ClusterIssuer is a cluster-wide resource that can issue certificates in any Kubernetes namespace:

yaml — cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: devops@yourdomain.com
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - http01:
        ingress:
          class: nginx

Apply the manifest and verify the ClusterIssuer status is Ready: True:

bash
kubectl apply -f cluster-issuer.yaml
kubectl get clusterissuer letsencrypt-prod

5. Step 4: Create TLS-Secured Ingress Resource

Annotate your application Ingress to trigger automatic certificate issuance:

yaml — app-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production-api-ingress
  namespace: default
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "20m"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.yourdomain.com
    secretName: api-tls-cert
  rules:
  - host: api.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80

6. Step 5: Verification & Smoke Testing

Inspect certificate status and confirm issuance:

bash
# Check certificate generation progress
kubectl get certificate api-tls-cert -o wide

# Check CertificateRequest, Order, and Challenge if debugging
kubectl get certificaterequest
kubectl get orders
kubectl get challenges

# Verify live TLS handshake
curl -Iv https://api.yourdomain.com

7. Production Troubleshooting Runbook

  • Challenge Fails with 404 Not Found: The DNS record for api.yourdomain.com must point directly to the LoadBalancer of Ingress-NGINX. If DNS does not resolve to the cluster, Let's Encrypt cannot verify the token.
  • Rate Limit Exceeded: Let's Encrypt Production has a limit of 5 failed validations per hour per account. Use letsencrypt-staging for testing.
  • Ingress Class Mismatch: In Kubernetes 1.18+, ensure spec.ingressClassName: nginx matches the ClusterIssuer HTTP01 ingress class.