
September 10, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Manual TLS on Kubernetes breaks at scale. You paste a certificate into a Secret, wire it to an Ingress, and three months later someone gets a pager because renewal never happened. cert-manager: Automate Kubernetes TLS solves that loop by watching Certificate resources and talking to public or private CAs on your behalf. If you already run Ingress with TLS on Kubernetes, cert-manager is the piece that removes copy-paste from the pipeline. This guide covers install, issuers, challenge solvers, Ingress wiring, and the failures I see on real clusters.
What is cert-manager and why should you use it to automate Kubernetes TLS?
cert-manager is a Kubernetes-native controller. It extends the API with custom resources for certificates, issuers, and certificate requests. You declare what hostname needs TLS. The controller obtains the cert, stores it in a Secret, and renews it automatically.
On bare-metal Ubuntu hosts I still use Certbot with Apache or Nginx. That model does not map cleanly to pods that restart and Secrets that must exist before the Ingress controller starts. cert-manager fits the Linux and cluster operations mindset: declare desired state, let the controller reconcile.
The main objects you will touch are:
- Issuer / ClusterIssuer — defines the CA (Let's Encrypt staging or production, or an internal CA).
- Certificate — names the Secret, DNS names, and which issuer to use.
- CertificateRequest — created by cert-manager during issuance; useful for debugging.
- Order and Challenge — ACME workflow artifacts when using HTTP-01 or DNS-01.
Without cert-manager, teams often script Certbot on a bastion host and push Secrets with kubectl. That works until DNS changes, pods move zones, or someone forgets the cron job. cert-manager keeps issuance inside the cluster reconciliation loop, which pairs well with GitOps workflows on Kubernetes.
How do you install cert-manager on a Kubernetes cluster?
Install cert-manager into its own namespace. Pin a release version from the official docs rather than floating to latest on production. The steps below use the upstream manifest approach; Helm is equally valid for teams that already standardise on it.
Step 1: Install CRDs and the controller
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.crds.yaml
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.16.2 \
--set crds.enabled=false
Wait until all pods report Ready:
kubectl -n cert-manager get pods
kubectl -n cert-manager wait --for=condition=Ready pod --all --timeout=120s
Step 2: Verify the API is registered
kubectl get crd | grep cert-manager
kubectl api-resources | grep cert-manager
You should see certificates.cert-manager.io, clusterissuers.cert-manager.io, and related CRDs. If the controller CrashLoopBackOffs, check webhook connectivity—the same class of issue covered in debugging CrashLoopBackOff in Kubernetes.
Step 3: Create a staging ClusterIssuer first
Always test against Let's Encrypt staging. Production rate limits bite hard during iterative deploys.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: ops@example.com
privateKeySecretRef:
name: letsencrypt-staging-account-key
solvers:
- http01:
ingress:
class: nginx
Apply it, then confirm the issuer is ready:
kubectl apply -f clusterissuer-staging.yaml
kubectl describe clusterissuer letsencrypt-staging
Look for Ready=True in conditions. If not, read Events on the ClusterIssuer and cert-manager controller logs.
How does cert-manager issue Let's Encrypt certificates with HTTP-01 and DNS-01?
ACME issuance follows a predictable sequence. cert-manager creates an Order, spawns Challenges, proves domain control, then downloads the signed certificate into a Secret. The solver you pick determines how that proof reaches the CA.
HTTP-01: best for single-host Ingress with public port 80
HTTP-01 is the default for public web apps behind an Ingress controller. Let's Encrypt fetches a token at http://yourdomain/.well-known/acme-challenge/.... cert-manager creates a temporary Ingress or edits an existing one.
Annotate your Ingress to request a certificate:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-example-com-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80
cert-manager creates a Certificate resource automatically from the TLS stanza. You can also define Certificate explicitly when you need finer control over SANs or duration.
DNS-01: required for wildcards and private clusters
DNS-01 proves control by creating a TXT record. Use it for wildcard certs like *.example.com or when port 80 is not reachable. Each DNS provider needs a cert-manager webhook or built-in solver.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod-dns
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@example.com
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
selector:
dnsZones:
- example.com
Store API tokens in a Kubernetes Secret, not in plain YAML committed to Git. For production hardening patterns, see managing secrets with AWS Secrets Manager or your cloud's equivalent.
| Solver | Port requirement | Wildcard support | Typical use case |
|---|---|---|---|
| HTTP-01 | Public TCP 80 | No | Single-host web apps behind Ingress |
| DNS-01 | None (API access to DNS) | Yes | Wildcard certs, internal-only services |
| TLS-ALPN-01 | Public TCP 443 | No | Specialised ingress setups (rare) |
Official ACME behaviour is documented by Let's Encrypt challenge types. cert-manager's own ACME configuration guide lists supported issuers and solver options.
Which Issuer should you choose: ClusterIssuer vs Issuer for namespace-scoped TLS?
ClusterIssuer is cluster-wide. Any namespace can reference it by name. Issuer is namespaced—only pods in that namespace can use it. For shared Let's Encrypt accounts and consistent policy, ClusterIssuer is the usual production choice.
Production ClusterIssuer for Let's Encrypt with HTTP-01:
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
Explicit Certificate resource when you need multiple SANs or a custom Secret name:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: app-example-com
namespace: production
spec:
secretName: app-example-com-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- app.example.com
- www.app.example.com
renewBefore: 720h
The renewBefore field defaults to roughly 30 days before expiry. I often set 720h (30 days) explicitly so renewals surface in monitoring early. Pair this with alerts on Certificate Ready=False conditions.
For Laravel apps moving to Kubernetes, the same hostname you used with Apache Certbot becomes the dnsNames entry. The app container still reads TLS from the Secret the Ingress mounts—your PHP code does not change. See Kubernetes for Laravel getting started for the broader migration path.
How do you troubleshoot cert-manager when certificates fail or stop renewing?
Most failures fall into four buckets: DNS not pointing at the cluster, port 80 or 443 blocked, wrong Ingress class in the solver config, or rate limits from repeated failed orders. Work through them in that order before chasing obscure controller bugs.
- Check Certificate status:
kubectl describe certificate -n production app-example-com - Inspect Challenges:
kubectl get challenges -Aand read Events on stuck rows. - Confirm HTTP reachability:
curl -I http://app.example.com/.well-known/acme-challenge/testfrom outside the cluster. - Read controller logs:
kubectl -n cert-manager logs deploy/cert-manager --tail=100 - Verify issuer readiness:
kubectl describe clusterissuer letsencrypt-prod
A common mistake: staging and production issuers share the same email but different ACME servers, yet the Ingress annotation still points at staging after go-live. The cert looks valid in the browser but chains to a fake CA. Swap the annotation to letsencrypt-prod and delete the old Secret so cert-manager re-orders.
Renewal failures often appear weeks after a successful first issue. Monitor Certificate status.notAfter and set Prometheus alerts if you run kube-prometheus-stack. Backup TLS Secrets alongside app data—Velero backup and restore on Kubernetes captures Secrets if your backup policy includes them.
After certificates work, tune the edge for modern protocols. TLS 1.3 vs 1.2 on Nginx covers cipher choices once cert-manager keeps the cert fresh. Use the password generator for unrelated app secrets—never reuse ACME account keys or cert private keys as application passwords.
On platforms like Adventure Third Pole Trek, booking apps need reliable HTTPS for payment callbacks. Automated renewal removes a class of outage that manual processes invite. For teams without in-house cluster ops, support and maintenance or enterprise application development can cover the full stack from Ingress to application code.
Domain registration must complete before ACME validation runs. Point nameservers or A records at your load balancer first. Domain registration and hosting and testing and optimization both touch the DNS layer that cert-manager depends on.
If you operate the data plane on bare metal, MetalLB on bare-metal Kubernetes gives HTTP-01 a stable external IP. Without that IP, Let's Encrypt cannot complete challenges no matter how correct your YAML is.
Gateway API is gradually replacing classic Ingress. cert-manager supports Gateway resources through annotations on HTTPRoute or by referencing Certificates directly. The reconciliation model stays the same—only the attachment point changes. Read the Kubernetes Gateway API explained if you are planning that migration.
For broader cluster health, keep cert-manager on your runbook alongside network policy and resource tuning from Kubernetes troubleshooting field guide and performance tuning. TLS automation should be boring. Boring means renewals happen without tickets.
When auditing security, confirm RBAC limits who can create ClusterIssuers. A malicious Issuer pointed at an attacker-controlled CA is a effective MITM vector inside the cluster. Restrict clusterissuers create permission to platform admins only.
Private CA issuers (for internal mTLS or service meshes) follow the same Certificate CRD pattern. You swap the ACME block for a ca stanza referencing a root Secret. Internal certs still renew on schedule—do not assume long-lived self-signed means set-and-forget.
Cost note: Let's Encrypt is free. Your spend is operational—load balancer hours, DNS API calls, engineer time on failed challenges. On a small Nepal SaaS budget (Rs 15,000–25,000/month hosting, ~USD 110–185), automating TLS removes recurring manual work that agencies otherwise bill hourly for.
Compare this approach to VM-level Certbot cron jobs in my background across Linux hosting and Laravel deployments. cert-manager is the Kubernetes-native expression of the same principle: certificates are infrastructure code, not calendar reminders.
Need a sanity check on your manifests? Paste YAML into the JSON formatter when converting between tooling outputs, or validate regex on hostnames with the regex tester before you apply.
Explore more cluster content on the blog, review shipped work in the portfolio, or read how Kokil Thapa approaches full-stack delivery from DNS to deployment.
Key Takeaways
- Install cert-manager with pinned CRDs and verify Issuer
Ready=Truebefore production Ingress cutover. - Use Let's Encrypt staging first; switch the Ingress annotation to production only after a clean HTTP-01 or DNS-01 pass.
- Prefer ClusterIssuer for shared policy; use explicit Certificate resources when you need multiple SANs or custom renewal windows.
- HTTP-01 needs public port 80 to the solver; DNS-01 is mandatory for wildcard certificates.
- Monitor Certificate conditions and
notAfter; renewal failures show up weeks after the initial success. - Restrict ClusterIssuer RBAC and back up TLS Secrets alongside application data.
People Also Ask
Does cert-manager replace Certbot on Kubernetes?
Yes, for in-cluster workloads. cert-manager performs the same ACME role Certbot plays on VMs, but it writes Kubernetes Secrets and integrates with Ingress or Gateway controllers. You may still use Certbot on non-Kubernetes servers.
How often does cert-manager renew Let's Encrypt certificates?
Let's Encrypt certs are valid for 90 days. cert-manager renews by default when roughly 30 days remain. Override with the renewBefore field on the Certificate resource.
Can cert-manager issue wildcard TLS certificates?
Yes, but only through DNS-01 validation. HTTP-01 cannot prove control over a wildcard domain. Configure a DNS solver matching your provider and list the wildcard in dnsNames.
What happens if cert-manager renewal fails overnight?
The existing certificate remains valid until its expiry date. Ingress keeps serving the old cert. Fix the root cause before expiry—check Challenge Events, DNS, and firewall rules—then cert-manager retries on the next reconciliation loop.
Put cert-manager to work on your cluster
cert-manager: Automate Kubernetes TLS turns certificate management into declarative config you can Git-review and redeploy. Start with staging, prove HTTP-01 or DNS-01 end to end, then promote to production and add monitoring on Certificate readiness. If you want help wiring Ingress, issuers, and Laravel or API workloads on the same cluster, contact us or browse web development services to scope the work.
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.

