Skip to main content

Add SSL To Kubernetes Deployment

· 2 min read
Femi Adigun
Senior Software Engineer & Coach

step-by-step guide to setting up HTTPS with cert-manager and Let’s Encrypt on your Kubernetes cluster using NGINX Ingress.


📘 Full Guide: Enabling HTTPS with cert-manager + Let’s Encrypt

🧱 Prerequisites

  • Kubernetes cluster (e.g., Linode LKE)
  • NGINX Ingress Controller installed via Helm
  • DNS A record pointing to your domain. Mine is demo.zemo.app → your LoadBalancer IP
  • Helm installed and configured

1️⃣ Install cert-manager

kubectl create namespace cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml

Verify:

kubectl get pods -n cert-manager

2️⃣ Create a ClusterIssuer

Create k8s/clusterissuer.yaml:

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

Apply:

kubectl apply -f k8s/clusterissuer.yaml

3️⃣ Patch NGINX Config to Allow ACME Challenge

A. Confirm NGINX is installed via Helm

helm list -n default

B. Patch the ConfigMap

kubectl patch configmap ingress-nginx-controller -n default \
--type merge \
-p '{"data":{"strict-validate-path-type":"false"}}'

Restart the controller:

kubectl rollout restart deployment ingress-nginx-controller -n default

4️⃣ Disable NGINX Admission Webhook (if blocking cert-manager)

helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
--namespace default \
--set controller.admissionWebhooks.enabled=false

Confirm webhook is removed:

kubectl get validatingwebhookconfiguration

5️⃣ Create Your Ingress Resource

Create k8s/ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: zemo-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "false"
cert-manager.io/http01-edit-in-place: "true"
spec:
ingressClassName: nginx
rules:
- host: demo.zemo.app
http:
paths:
- path: /health
pathType: Prefix
backend:
service:
name: zemo-app-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: zemo-app-service
port:
number: 80
tls:
- hosts:
- demo.zemo.app
secretName: zemo-app-tls

Apply:

kubectl apply -f k8s/ingress.yaml

6️⃣ Monitor Certificate Issuance

Check certificate status:

kubectl get certificate
kubectl describe certificate zemo-app-tls

Check challenge:

kubectl get challenge
kubectl describe challenge <name>

7️⃣ Final Steps

Once the certificate is issued (READY: True):

  • Remove ssl-redirect: "false" from annotations
  • Optionally add:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"

Reapply Ingress:

kubectl apply -f k8s/ingress.yaml

Visit:

https://demo.zemo.app

🧠 Optional Enhancements

  • Use staging issuer for testing:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
  • Add cert-manager.io/issue-temporary-certificate: "true" to serve HTTPS while waiting
  • Automate cert renewal and Ingress updates via GitOps or CI/CD