Certificate lifecycle

Pick the right ACME solver before debugging expired certs at 3am.

Reviewed: ·Tested on: Kubernetes 1.29, Terraform 1.8, Ubuntu 22.04

Decision tree

Is the cert for a public hostname on the internet?

Yes → public-acme · No → internal-ca

Do you need a wildcard cert (*.example.com)?

Yes → use-dns01 · No → http-or-dns

Can Let's Encrypt reach your app on HTTP port 80 from the internet?

Yes → use-http01 · No → use-dns01

use http01

Use cert-manager ClusterIssuer with HTTP-01 solver — simplest for single-host ingress.

use dns01

Use DNS-01 with Route53/Cloudflare API — required for wildcards and internal-only LBs.

internal ca

Use internal CA (Vault PKI, cert-manager self-signed, or corporate CA) — not public ACME.

Scenarios

🌟

Wildcard certificate

Single cert for *.example.com and apex.

cert-manager Certificate with dnsNames: ['example.com', '*.example.com'] + DNS-01 ClusterIssuer only.

When: Many subdomains · Same ingress controller · DNS provider API available

🏢

Internal / mTLS CA

Service-to-service or private cluster certs.

Vault PKI engine or cert-manager CA Issuer; distribute trust bundle to clients.

When: No public internet · Compliance requires private PKI · Mesh or internal APIs

🌐

HTTP-01 (Let's Encrypt)

Auto-renew via ingress HTTP challenge.

Ingress shim or cert-manager ingress template annotation cert-manager.io/cluster-issuer.

When: Single hostname · Port 80 reachable · No wildcard needed

📡

DNS-01 (wildcard + private LB)

ACME via DNS TXT record.

ClusterIssuer with solvers.dns01 provider credentials in cert-manager namespace.

When: Wildcard required · LB not public · Cloudflare/Route53 API token in secret

🖥️

Manual / VM certbot

Standalone nginx or VM without K8s cert-manager.

certbot renew via cron; reload nginx after renew.

When: Legacy VM · No cluster · Simple single site