Cert-Manager in K3s isn’t just about getting TLS certificates; it fundamentally changes how your cluster handles identity and trust for all its internal and external communication.

Let’s see it in action. Imagine you have a simple Nginx Ingress controller deployed, and you want to serve a website myapp.local over HTTPS.

First, the basic Ingress resource, but with a tls section pointing to a non-existent certificate:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  namespace: default
spec:
  tls:
  - hosts:
    - myapp.local
    secretName: myapp-tls-secret
  rules:
  - host: myapp.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myapp-service
            port:
              number: 80

Now, you need Cert-Manager. You install it, typically via Helm:

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.11.0 \
  --set installCRDs=true

Once Cert-Manager is running, you define a Issuer or ClusterIssuer. A ClusterIssuer is cluster-wide, while an Issuer is namespace-specific. For Let’s Encrypt, you’d configure it like this for a ClusterIssuer:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-private-key
    solvers:
    - http01:
        ingress:
          class: nginx

With Cert-Manager installed and the ClusterIssuer applied, you don’t need to do anything else to the Ingress. Cert-Manager watches for Ingress resources that specify a secretName in their tls section. It sees myapp-tls-secret for myapp.local. It then looks for a ClusterIssuer (or Issuer) that can satisfy the request. Finding letsencrypt-prod, it initiates the ACME challenge.

For http01 challenges, Cert-Manager temporarily modifies your Ingress (or another specified resource) to respond to Let’s Encrypt’s validation requests. Once validated, Let’s Encrypt issues the certificate, Cert-Manager stores it in the myapp-tls-secret Kubernetes Secret, and your Ingress controller automatically picks it up. Your myapp.local is now served over HTTPS.

The core problem Cert-Manager solves is the lifecycle management of TLS certificates, which are notoriously difficult to manage manually. This includes obtaining new certificates, renewing expiring ones, and revoking compromised certificates. It abstracts away the complexities of ACME (Automated Certificate Management Environment) protocols, DNS challenges, and HTTP challenges, making secure communication a declarative feature of your Kubernetes cluster. Internally, it operates by watching for CertificateRequest resources created by Certificates (a custom resource it defines). These CertificateRequest resources detail the desired certificate parameters. The Cert-Manager controller then interacts with the configured Issuer or ClusterIssuer to fulfill the request, whether that’s by talking to Let’s Encrypt, a private CA, or another ACME server.

When you set up an ACME ClusterIssuer with http01 and specify ingress.class: nginx, Cert-Manager doesn’t directly modify your Ingress. Instead, it creates a temporary Ingress resource (or modifies an existing one if configured) that is solely for the purpose of responding to the ACME challenge. This temporary Ingress is specifically routed to a Cert-Manager pod that handles the challenge. Once the challenge is complete, this temporary resource is cleaned up. This separation ensures your application’s Ingress remains untouched by the validation process.

The most surprising thing is that Cert-Manager doesn’t actually store the private keys for the certificates it issues itself by default. When you configure an ACME ClusterIssuer, you specify privateKeySecretRef for the issuer’s own private key. However, for the certificates it obtains, it stores them as standard Kubernetes Secrets. The actual private keys for your application’s certificates are embedded within these Secrets, and Cert-Manager manages the lifecycle of these Secrets by creating, updating, or deleting them as certificates are issued, renewed, or revoked.

The next step is exploring how to use private CAs or integrate with cloud provider certificate management services.

Want structured learning?

Take the full K3s course →