Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

cert-manager: Automate Kubernetes TLS

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.
cert-manager: Automate Kubernetes TLSCertificateCRD resourceClusterIssuerLet's Encryptcert-managercontroller podACME CALet's EncryptTLS Secrettls.crt + tls.keyIngress / Gatewayterminates HTTPS
cert-manager watches Certificate and Issuer CRDs, completes ACME validation, and writes TLS Secrets for Ingress or Gateway controllers.

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 ACME Challenge FlowUser / BrowserIngresscert-managerLet's EncryptChallenge Pod/.well-known/acmeTLS Secrettls.crt mountedCertificateReady=True1. Order2. Route3. Serve token4. Validate5. Store6. ReadyPort 80 must reach the solver Ingress from the public internetFirewalls and DNS must resolve before the challenge expires
HTTP-01 validation routes ACME tokens through a temporary Ingress or solver pod so Let's Encrypt can reach your cluster on port 80.

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.

SolverPort requirementWildcard supportTypical use case
HTTP-01Public TCP 80NoSingle-host web apps behind Ingress
DNS-01None (API access to DNS)YesWildcard certs, internal-only services
TLS-ALPN-01Public TCP 443NoSpecialised 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.

ClusterIssuer vs Issuer ScopeClusterIssuernamespace: appnamespace: apiletsencrypt-prod (shared)One ACME account, all teamsIssuer (namespaced)namespace: appno accessissuer-app (local only)Isolated CA per namespacevs
ClusterIssuer centralises TLS policy for every namespace; Issuer limits certificate authority configuration to a single namespace.

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.

  1. Check Certificate status: kubectl describe certificate -n production app-example-com
  2. Inspect Challenges: kubectl get challenges -A and read Events on stuck rows.
  3. Confirm HTTP reachability: curl -I http://app.example.com/.well-known/acme-challenge/test from outside the cluster.
  4. Read controller logs: kubectl -n cert-manager logs deploy/cert-manager --tail=100
  5. Verify issuer readiness: kubectl describe clusterissuer letsencrypt-prod
cert-manager TLS Failure TriageCertificate Ready=FalseDNS resolves?A/AAAA to LB IPPort 80 open?HTTP-01 reachableIssuer ready?ACME account OKFix DNS / TTLOpen firewallCheck rate limitsCertificate Ready=True
When cert-manager TLS automation stalls, verify DNS, challenge reachability, and issuer health before deep-diving controller internals.

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=True before 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

cert-manager is a Kubernetes-native controller that extends the API with custom resources for certificates, issuers, and certificate requests. You declare which hostname needs TLS, and the controller obtains the certificate, stores it in a Secret, and renews it automatically. Manual TLS on Kubernetes breaks at scale because someone eventually forgets renewal. cert-manager keeps issuance inside the cluster reconciliation loop, which pairs well with GitOps workflows and removes copy-paste from your Ingress pipeline.

Install cert-manager into its own namespace and pin a release version rather than floating to latest on production. Apply the upstream CRDs manifest for v1.16.2, add the jetstack Helm repo, then install the chart with crds.enabled=false since CRDs were applied separately. Wait until all pods in the cert-manager namespace report Ready, then verify the API is registered with kubectl get crd and kubectl api-resources filtered for cert-manager. Create a staging ClusterIssuer first and confirm Ready=True before production cutover.

Yes, for in-cluster workloads. cert-manager performs the same ACME role Certbot plays on VMs, but writes Kubernetes Secrets and integrates with Ingress or Gateway controllers instead of Apache or Nginx config files.

Let's Encrypt certificates are valid for 90 days. cert-manager renews by default when roughly 30 days remain. Override timing with the renewBefore field on the Certificate resource.

Yes, but only with DNS-01 validation. Wildcard certs like star.example.com require proving domain control via a TXT record, not HTTP-01 on port 80.

HTTP-01 routes ACME tokens through a temporary Ingress or solver pod so Let's Encrypt can reach your cluster on public port 80. It works for single-host Ingress setups but does not support wildcards. DNS-01 proves control by creating a TXT record via your DNS provider API, which is mandatory for wildcard certificates and useful when port 80 is unreachable. Each DNS provider needs a cert-manager webhook or built-in solver, with API tokens stored in Kubernetes Secrets rather than plain YAML in Git.

ClusterIssuer is cluster-wide and any namespace can reference it by name, making it the usual production choice for shared Let's Encrypt accounts and consistent policy. Issuer is namespaced, limiting certificate authority configuration to a single namespace. For teams running multiple apps across namespaces behind the same Ingress controller, ClusterIssuer centralises TLS policy. Use an explicit Certificate resource in a specific namespace when you need multiple SANs, a custom Secret name, or a custom renewBefore window like 720h.

Annotate your Ingress with cert-manager.io/cluster-issuer pointing at your ClusterIssuer name, then define a tls stanza with hosts and secretName. cert-manager creates a Certificate resource automatically from that TLS block. For finer control over SANs, duration, or renewal timing, define a Certificate resource explicitly with issuerRef, dnsNames, secretName, and optionally renewBefore set to 720h so renewals surface in monitoring early.

Production Let's Encrypt rate limits bite hard during iterative deploys. A staging ClusterIssuer pointing at the acme-staging-v02 server lets you validate HTTP-01 or DNS-01 solver configuration without burning production quotas. Confirm Ready=True on the staging issuer before switching your Ingress annotation to the production ClusterIssuer. A common go-live mistake is leaving the staging annotation in place, which produces certs that chain to a fake CA and look wrong to browsers even though the hostname matches.

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. Run kubectl describe certificate on the affected resource, inspect Challenges cluster-wide and read Events on stuck rows, curl the ACME challenge path from outside the cluster, check cert-manager controller logs, and verify your ClusterIssuer shows Ready=True. Renewal failures often appear weeks after a successful first issue, so monitor Certificate status.notAfter and set Prometheus alerts if you run kube-prometheus-stack.

The main objects are Issuer and ClusterIssuer, which define the CA such as Let's Encrypt staging or production or an internal CA. Certificate names the Secret, DNS names, and which issuer to use. CertificateRequest is created during issuance and is useful for debugging. Order and Challenge are ACME workflow artifacts when using HTTP-01 or DNS-01. cert-manager watches Certificate and Issuer CRDs, completes ACME validation, and writes TLS Secrets for Ingress or Gateway controllers to mount.

A malicious ClusterIssuer pointed at an attacker-controlled CA is an effective MITM vector inside the cluster. Restrict clusterissuers create permission to platform admins only through RBAC. Store DNS provider API tokens and ACME account keys in Kubernetes Secrets, not in plain YAML committed to Git. Never reuse ACME account keys or certificate private keys as application passwords. Private CA issuers for internal mTLS follow the same Certificate CRD pattern with a ca stanza referencing a root Secret, and those internal certs still renew on schedule.

Let's Encrypt itself is free. Your spend is operational: load balancer hours, DNS API calls, and engineer time debugging failed challenges. On a small Nepal SaaS budget of Rs 15,000 to 25,000 per month hosting, roughly USD 110 to 185, automating TLS removes recurring manual work that agencies otherwise bill hourly for. Compare this to VM-level Certbot cron jobs on bare-metal Ubuntu hosts where you still maintain the renewal script and Secret push pipeline separately from the application.

Domain registration must complete and DNS must point at your cluster before ACME validation runs. Configure nameservers or A records at your load balancer first. HTTP-01 requires public TCP port 80 reachable by Let's Encrypt. On bare-metal clusters without a cloud load balancer, MetalLB can provide a stable external IP for HTTP-01 solvers. Without that reachable IP or correct DNS, Let's Encrypt cannot complete challenges regardless of how correct your YAML manifests are.

Backup TLS Secrets alongside application data using Velero if your backup policy includes Secrets. Monitor Certificate Ready conditions and set alerts when Ready=False. Track status.notAfter and configure Prometheus alerts through kube-prometheus-stack so renewal failures surface before expiry. Pair explicit renewBefore values like 720h with monitoring so you catch stalled renewals weeks after the initial successful issuance rather than at certificate expiry.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: