Add SSL To Kubernetes Deployment
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