Cert Manager - TLS via ArgoCD#
What We Built#
This doc covers the learning exercise: deploying cert-manager as a manually applied ArgoCD
Application, creating a self-signed CA issuer, and enabling TLS for eu-dev services
via values file changes.
Current approach: in
eu-dev-rancher, cert-manager is managed as wave 0 in the sync wave sequence - no manualkubectl applyneeded. Theconfig/cert-manager/ClusterIssuer manifests used here are reused as-is byeu-dev-rancher. Seedocs/09-sync-waves-cluster-complete.md.
| |
How cert-manager Works#
cert-manager runs as a controller inside the cluster. It watches for Certificate and Ingress resources and automatically creates TLS certificates.
Three key concepts:
| Resource | What it is |
|---|---|
ClusterIssuer | Cluster-scoped authority that signs certificates (e.g. self-signed, Let’s Encrypt) |
Certificate | A request for a cert - cert-manager creates a Secret with the actual TLS key/cert |
| Ingress annotation | cert-manager.io/cluster-issuer: <name> - tells cert-manager to auto-issue a cert for this Ingress |
For local development we use a self-signed CA issuer:
- Create a root CA cert (signed by itself)
- Create a
ClusterIssuerbacked by that CA - All app certs are signed by the CA
- Trust the CA once in macOS Keychain → browser trusts all app certs automatically
Files Created#
| |
The apps/cert-manager/ and apps/cert-manager-config/ Application manifests were
applied manually as a learning exercise. In eu-dev-rancher, these are replaced by
environments/eu-dev-rancher/platform/cert-manager.yaml (wave 0) and
environments/eu-dev-rancher/platform/cert-manager-config.yaml (wave 1), which point
at the same config/cert-manager/ ClusterIssuer manifests.
The ApplicationSet (
environments/eu-dev/appset.yaml) auto-discovers values files - no changes to the AppSet manifest are needed. Updating a values file is enough to enable TLS.
Step-by-Step Instructions#
1. Deploy cert-manager via ArgoCD#
cert-manager is distributed as a Helm chart. We deploy it as an ArgoCD Application pointing at the official Helm repo.
Create apps/cert-manager/application.yaml:
| |
installCRDs: true- cert-manager’s Custom Resource Definitions (Certificate,ClusterIssuer, etc.) must exist before any cert resources can be created. This flag installs them as part of the Helm release.
Apply this Application to ArgoCD:
| |
Wait for cert-manager pods to be ready:
| |
2. Create a Self-Signed CA and ClusterIssuer#
We use a two-step bootstrap:
- A temporary
selfsignedClusterIssuer - no config needed, just signs certs with themselves - Use it to issue a root CA
Certificate - A
ca-issuerClusterIssuer backed by that CA cert - all app certs are signed by this
This mirrors how real internal PKI works: you generate a root CA once, then use it to sign everything else.
Create config/cert-manager/self-signed-issuer.yaml:
| |
Create config/cert-manager/ca-cert.yaml:
| |
This tells cert-manager: use the
selfsignedissuer to create a CA certificate, store the key+cert inlocal-ca-secretin thecert-managernamespace.
Create config/cert-manager/ca-issuer.yaml:
| |
This tells cert-manager: when signing certs, use the key stored in
local-ca-secretas the signing CA.
Create config/cert-manager/kustomization.yaml:
| |
Create apps/cert-manager-config/application.yaml:
| |
Commit and push, then apply:
| |
Verify the CA cert was issued and the issuer is ready:
| |
3. Enable TLS for eu-dev Services via the ApplicationSet#
The eu-dev-alpha-services ApplicationSet watches environments/eu-dev/values/*.yaml. Each service is rendered through charts/backend-service, which already supports ingress.tls. To enable TLS for a service, update its values file - the AppSet picks up the change automatically on the next sync.
Pattern: updated values file with TLS (example: environments/eu-dev/values/svc1.yaml):
| |
Key changes vs the HTTP-only values file:
| Field | Old (HTTP) | New (TLS) |
|---|---|---|
ingress.annotations | router.entrypoints: web | router.entrypoints: websecure + cluster-issuer annotation |
ingress.tls | absent | secretName + hosts |
Apply by committing and pushing the updated values file:
| |
ArgoCD will sync alpha-svc1-eu-dev automatically. cert-manager sees the cluster-issuer annotation on the Ingress and issues a certificate, storing it in the svc1-eu-dev-tls Secret.
Verify the cert was issued:
| |
Test HTTPS (with /etc/hosts entry for svc1.eu-dev.ravikrs.local → 127.0.0.1):
| |
Commands Reference#
| |
Gotchas#
CRDs must exist before
Certificate/ClusterIssuerresources are applied. Ifcert-manager-configsyncs before cert-manager itself is fully running, you’ll seeno matches for kind "ClusterIssuer"errors. ArgoCD will retry automatically - wait a minute and re-sync. For production, use sync waves to enforce ordering.ca-issuerdepends onlocal-ca-secretexisting. Thecert-manager-configapplication deploys all three resources together. Theca-cert.yamlmust reconcile (and populatelocal-ca-secret) beforeca-issuer.yamlcan become ready. cert-manager handles this with retries - you may seeca-issuershowREADY=Falsebriefly before the CA cert is signed.ClusterIssueris cluster-scoped - it can sign certs in any namespace. The CACertificatelives incert-managernamespace but the issuers it backs can serve apps inalpha-dev,alpha-staging, or any other namespace.Let’s Encrypt does not work for
.localdomains. ACME HTTP-01 and DNS-01 challenges require publicly resolvable DNS. For local-only hostnames, self-signed CA is the right approach.