
September 09, 2026
13 min read
By Kokil Thapa | Last reviewed: September 2026
Exposing a Laravel API or WordPress site on Kubernetes without TLS leaves traffic readable and breaks browser trust. Kubernetes Ingress and TLS with cert-manager automates HTTPS by pairing an Ingress controller with Let's Encrypt certificates that renew before they expire. In a typical Kubernetes cluster architecture, the Ingress sits at the edge while cert-manager watches Certificate resources and talks to ACME CAs. On clusters I have run alongside traditional Apache and Nginx VMs, this pattern replaces manual Certbot cron jobs with declarative YAML you can version in Git.
What is Kubernetes Ingress and TLS with cert-manager?
Ingress is a Kubernetes API object that routes HTTP and HTTPS from outside the cluster to Services inside it. TLS termination happens at the Ingress controller, which reads a Secret containing the certificate and private key. cert-manager is a controller that creates and renews those Secrets using ACME protocols from CAs like Let's Encrypt.
The split of responsibilities matters in practice. Ingress defines hostnames, paths, and which Secret to mount. cert-manager creates the Certificate CR, orders the cert, completes ACME challenges, and writes the tls.crt and tls.key into the Secret. When renewal time arrives, cert-manager repeats the cycle without human intervention.
If you already manage TLS on bare-metal Ubuntu with Certbot, the mental model maps cleanly. cert-manager is Certbot as a Kubernetes operator. The Ingress controller is Nginx or Apache at the edge. The difference is everything is declared in YAML and reconciled continuously, which fits a GitOps with ArgoCD workflow well.
How do you install an Ingress controller and cert-manager?
Start with a working cluster. Minikube, kind, k3s, EKS, and GKE all support this stack. You need kubectl access and Helm 3 installed locally. Pick one Ingress controller and one cert-manager release — mixing two controllers without clear IngressClass separation causes routing conflicts.
Install cert-manager
cert-manager ships official Helm charts. Pin a version rather than floating latest in production. The chart installs CRDs for Certificate, CertificateRequest, Issuer, ClusterIssuer, and Order resources.
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true \
--version v1.16.2
kubectl get pods -n cert-manager
Wait until cert-manager, cert-manager-webhook, and cert-manager-cainjector pods reach Running. Webhook failures during Issuer creation usually mean the webhook pod is not ready yet.
Install an Ingress controller
ingress-nginx remains the most common choice on self-managed clusters. Cloud load-balancer Ingress controllers on EKS and GKE integrate with their respective LBs. Match your IngressClass to the controller you install.
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 \
--set controller.ingressClassResource.name=nginx \
--set controller.ingressClassResource.controllerValue=k8s.io/ingress-nginx
kubectl get svc -n ingress-nginx
Point your DNS A or CNAME record at the external IP or hostname of the controller Service. On bare metal without a cloud LB, MetalLB on bare-metal Kubernetes can advertise a pool IP. Certificate issuance will fail until DNS resolves to the cluster edge.
How do you configure a ClusterIssuer for Let's Encrypt?
An Issuer is namespace-scoped. A ClusterIssuer is cluster-wide and the usual choice for shared edge TLS. Define one ClusterIssuer for staging and one for production while testing. Let's Encrypt staging avoids rate limits during iteration.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@example.com
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: nginx
Apply it and verify the Issuer reaches Ready status:
kubectl apply -f clusterissuer-letsencrypt-prod.yaml
kubectl describe clusterissuer letsencrypt-prod
The http01 solver tells cert-manager to create temporary Ingress rules so Let's Encrypt can reach /.well-known/acme-challenge/ on port 80. Port 80 must be open and routed through your Ingress controller. Blocking HTTP while forcing HTTPS-only breaks HTTP-01 validation unless you use DNS-01 instead.
For wildcard certificates covering *.example.com, HTTP-01 cannot work. Use DNS-01 with your DNS provider's API credentials stored in a Kubernetes Secret. Route53, Cloudflare, and DigitalOcean all have documented cert-manager webhook or native solver support. Treat DNS API tokens as sensitive — follow the same discipline as Kubernetes Secrets and ConfigMaps done right.
How do you attach TLS to an Ingress resource?
Two patterns exist. The annotation pattern is simpler for single Ingress objects. The Certificate CR pattern gives explicit control over SANs, duration, and renewBefore windows.
Annotation-based TLS (recommended for most apps)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: laravel-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: laravel-app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: laravel-app
port:
number: 80
cert-manager sees the annotation, creates a Certificate named after the Secret, and manages the full lifecycle. The Secret name in spec.tls must match what cert-manager expects. Renaming the Secret orphan old Certificates and triggers re-issuance.
Explicit Certificate resource
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: laravel-app-cert
namespace: production
spec:
secretName: laravel-app-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- app.example.com
- api.example.com
renewBefore: 720h
Use explicit Certificate objects when one Secret covers multiple hostnames or when GitOps separates cert lifecycle from Ingress routing. On a Laravel app on Kubernetes, you typically terminate TLS at Ingress and run HTTP inside the pod — no app-level cert needed.
After applying, watch progress:
kubectl describe certificate laravel-app-tls -n production
kubectl describe certificaterequest -n production
kubectl get challenges -n production
A Certificate in Ready=True with a populated Secret means HTTPS is live. Test with curl:
curl -vI https://app.example.com
Confirm the issuer is Let's Encrypt and check expiry with openssl if needed. For cipher and protocol hardening after issuance, see TLS 1.3 vs 1.2 configuration for Nginx.
Which Ingress controller should you use with cert-manager?
cert-manager is controller-agnostic. It creates solver Ingress objects with the ingress class you specify in the ClusterIssuer. Your production controller must honor that class and expose ports 80 and 443 correctly.
| Controller | Best for | cert-manager notes | Ops complexity |
|---|---|---|---|
| ingress-nginx | Self-managed clusters, bare metal, DO/Linode | Mature HTTP-01 solver support; widely documented | Medium — you manage upgrades |
| Traefik | k3s default, edge microservices | Native IngressRoute CRD option; ACME can bypass cert-manager | Low on k3s; medium elsewhere |
| AWS Load Balancer Controller | EKS with ALB/NLB | DNS-01 common for wildcards; HTTP-01 needs correct target groups | Medium — IAM and SG wiring |
| GCE/GKE Ingress | GKE native | Google-managed certs alternative; cert-manager for portable YAML | Low on GKE; cloud-specific |
| HAProxy Ingress | High-throughput L7 routing | Works with cert-manager; smaller community than nginx | Medium |
For lightweight edge clusters, k3s lightweight Kubernetes ships Traefik by default. You can keep Traefik and still run cert-manager, or disable Traefik and install ingress-nginx for consistency with larger clusters. Pick one edge stack and document it — switching controllers mid-project forces certificate re-validation.
How do you troubleshoot cert-manager and Ingress TLS failures?
Most failures fall into a short list. DNS misconfiguration, wrong ingress class, rate limits, and premature HTTPS redirects cover the majority of support tickets I have seen on production clusters.
- Check Certificate status:
kubectl describe certificate <name>— read Events at the bottom for the exact ACME error. - Inspect Challenge objects: Pending challenges show the URL Let's Encrypt is trying to reach. curl that URL from outside the cluster.
- Verify DNS:
dig app.example.com +shortmust return your Ingress LB IP before ordering a cert. - Confirm ingress class: solver Ingress class must match your controller's IngressClass resource name.
- Test with staging first: swap ClusterIssuer server to
https://acme-staging-v02.api.letsencrypt.org/directoryduring setup. - Review controller logs:
kubectl logs -n ingress-nginx deploy/ingress-nginx-controllerfor routing errors on challenge paths.
A Challenge stuck in pending with 403 or 404 usually means the temporary Ingress rule is not reaching the cert-manager solver pod. A 400 from Let's Encrypt often means stale authorization — delete the Certificate and Challenge objects to force a clean retry. For deeper pod-level issues on the same cluster, the guide on debugging CrashLoopBackOff in Kubernetes helps isolate solver pod crashes.
Let's Encrypt production rate limits matter on large migrations. You get 50 certificates per registered domain per week. Bulk-moving 80 subdomains in one afternoon hits the cap. Stage with the staging issuer, then batch production cutover across days. Official limits are documented at Let's Encrypt rate limits.
Renewal and expiry monitoring
cert-manager renews certificates when roughly two-thirds of the lifetime remains. Default cert duration from Let's Encrypt is 90 days, so renewal typically starts around day 60. Failed renewals leave the old cert in place until it expires — then browsers show errors.
Export cert-manager metrics to Prometheus and alert on certificates expiring within 14 days. Pair this with Velero backup and restore for Kubernetes so you can recover cluster state if a bad Issuer change breaks all active certs at once. For EKS workloads, also review running Kubernetes on AWS with Amazon EKS because ALB annotations interact with Ingress TLS differently than bare ingress-nginx.
Store ClusterIssuer manifests and Certificate CRs in Git. A bad edit to solvers or email can silently break renewal for every hostname. Peer review those changes the same way you review application deploys. If your team lacks dedicated platform engineers, Linux system administration support and ongoing maintenance services cover cluster edge cases including cert-manager upgrades.
When moving a live site from a VM to Kubernetes, plan DNS TTL reduction a day ahead. Lower TTL to 300 seconds before cutover so you can roll back quickly if ACME validation fails. This mirrors the discipline used in website migration projects where downtime and cert gaps are the main client-facing risks.
For JSON manifest review before apply, a quick pass through the JSON formatter tool catches trailing commas and quoting errors that kubectl would reject. On booking platforms like Adventure Third Pole Trek, HTTPS at the Ingress edge is non-negotiable for payment trust — cert-manager removes the manual renewal step that VM-hosted Certbot required.
Domain registration and initial DNS setup must be correct before any ACME order. If you manage client domains, coordinate with domain registration and hosting services so nameservers and A records propagate before the first Certificate apply. A highly available Kubernetes control plane does not help if external DNS still points at the old server.
cert-manager upgrades require CRD migration attention. Read the release notes on cert-manager official documentation before bumping minor versions. The upstream Kubernetes Ingress documentation remains the reference for networking API field behavior across cluster versions.
Key Takeaways
- Install cert-manager and one Ingress controller first; verify DNS points at the LB before requesting production certs.
- Use a staging ClusterIssuer during setup to avoid Let's Encrypt rate limits while debugging HTTP-01 paths.
- Annotate Ingress with cert-manager.io/cluster-issuer for simple apps; use Certificate CRs for multi-hostname or wildcard TLS.
- Keep port 80 open for HTTP-01 unless you switch to DNS-01 with a properly scoped API token Secret.
- Monitor certmanager_certificate_expiration_timestamp_seconds and alert well before the 90-day expiry window.
- Version ClusterIssuer and Certificate YAML in Git and treat solver changes as production-impacting operations.
People Also Ask
Does cert-manager work with any Ingress controller?
Yes. cert-manager creates temporary Ingress or HTTPRoute resources using the ingress class defined in your ClusterIssuer solver config. As long as that class routes port 80 and 443 to the correct controller, ACME HTTP-01 validation succeeds. The controller brand does not matter — configuration alignment does.
Can cert-manager issue wildcard TLS certificates?
Wildcard certificates require DNS-01 validation because HTTP-01 cannot prove control over an entire subdomain tree. Configure a DNS solver with your provider API credentials in a Kubernetes Secret, then list *.example.com in the Certificate dnsNames field. Wildcard certs cover one level only — *.example.com does not include nested hosts like a.b.example.com.
What happens when a cert-manager certificate fails to renew?
The existing TLS Secret remains until the certificate expires. Browsers continue trusting the site until the notAfter date passes. After expiry, clients see certificate errors. cert-manager emits Kubernetes Events on the Certificate object and exposes Prometheus metrics you should alert on — do not rely on calendar reminders alone.
Is cert-manager better than certbot on Kubernetes?
On Kubernetes, cert-manager is the idiomatic choice. It watches Certificate CRs, integrates with Ingress annotations, and renews without shell access to nodes. Certbot inside a CronJob works but fights the declarative model and is harder to observe. cert-manager also centralises issuer config as cluster-level CRDs rather than scattered cron entries.
Put HTTPS on autopilot at the Ingress edge
Kubernetes Ingress and TLS with cert-manager turns HTTPS from a quarterly maintenance task into a reconciled cluster state. Install the controllers, define a ClusterIssuer, annotate your Ingress, and verify with describe and curl before you cut over DNS. Start with staging, watch Challenge objects during the first issuance, and add Prometheus alerts before production traffic depends on the cert. If you want help migrating a Laravel or WordPress workload from VM-based Certbot to a declarative cluster edge, contact us or browse the portfolio for platforms already running on production infrastructure.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

