cert-manager¶
Issues and renews TLS certificates for every public hostname in the cluster. No one has ever copy-pasted a .pem file into this repo — every TLS Secret you see (argocd-server-tls, grafana-tls, auth-tls, the app's TLS Secret) is generated by cert-manager from a Let's Encrypt HTTP-01 challenge.
Installed by k8s/envs/dev/sys/cert-manager/application.yaml. The ClusterIssuer is defined in k8s/envs/dev/sys/cert-manager/cluster-issuer.yaml.
Chart and source¶
| Field | Value |
|---|---|
| Helm chart | cert-manager |
| Repository | https://charts.jetstack.io |
| Version | v1.14.0 |
installCRDs |
true (passed as a Helm parameter) |
| Destination namespace | cert-manager (created by Argo CD) |
| Argo CD Application | cert-manager |
installCRDs: true matters — it ships the Certificate, Issuer, ClusterIssuer, CertificateRequest, Order, and Challenge CRDs. The rest of the cluster (especially the per-app Ingresses) depends on these existing before sync.
ClusterIssuer¶
One issuer, cluster-wide, for production Let's Encrypt:
# k8s/envs/dev/sys/cert-manager/cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@tomoda.life
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik
Three things to note:
- Production endpoint. This is the real
acme-v02.api.letsencrypt.org— certificates are real, and so are the Let's Encrypt rate limits (50 certs per registered domain per week, 5 duplicate certs per week). There is no staging issuer installed, so don't burn through the budget by creating and deleting certs in a loop. - HTTP-01 over Traefik. When cert-manager issues a cert it writes a temporary
Ingresssolving the ACME challenge. That challengeIngressdeclaresclass: traefik, which means the solver only works because Traefik is the cluster's ingress controller. If Traefik is down, no new certs can be issued. - No DNS-01. Wildcard certs aren't supported here. Every Ingress that wants TLS needs to list its exact host(s) — which is fine, every host we use is a literal subdomain of
tomoda.life.
How a workload requests a cert¶
A per-app Ingress declares two things:
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- argo-app.tomoda.life
secretName: argocd-server-tls
cert-manager's Ingress shim notices the annotation, creates a Certificate resource targeting argocd-server-tls, runs the HTTP-01 challenge, and writes the resulting cert+key into argocd-server-tls in the same namespace. Traefik picks the Secret up automatically and serves it for that host.
Concrete examples in this repo:
argocd-server-tlsforargo-app.tomoda.life(k8s/envs/dev/sys/argocd-ingress/ingress.yaml)grafana-tlsforgrafana.tomoda.life(k8s/envs/dev/sys/monitoring/values.yaml)auth-tlsforauth.tomoda.life(k8s/envs/dev/sys/oauth2-proxy/application.yaml)
Auto-renewal¶
cert-manager renews certs roughly 30 days before expiry (Let's Encrypt issues 90-day certs). Renewal is the same HTTP-01 flow as the initial issuance, so it has the same Traefik dependency. No human action is required — but if you see a cert in Status: False for Ready, check the Order and Challenge resources in that namespace.
kubectl get certificate -A
kubectl describe certificate <name> -n <namespace>
kubectl get order,challenge -A
Operational notes¶
- Removing a TLS Secret manually triggers re-issuance — cert-manager will notice the Secret is gone and re-run the challenge. Useful for forcing a refresh, but burns one against the rate limit.
- CRDs are installed via the chart, not separately. If you ever uninstall the cert-manager Application, the CRDs go with it, and every
Certificateresource in the cluster disappears in the same sync — followed by every TLS Secret. Don't do this without a plan. - No external secrets here. cert-manager doesn't authenticate to anything except Let's Encrypt over HTTP, so there are no GCP or AWS credentials involved.
For the broader TLS story (Cloudflare edge TLS, certificate inventory, what hostnames terminate where), see Security → TLS.