cert-manager is a Kubernetes controller that automates the management and provisioning of TLS certificates.

Let’s see it in action. Imagine you have a Kubernetes deployment that needs a TLS certificate to serve HTTPS traffic.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-app-tls
  namespace: default
spec:
  secretName: my-app-tls-secret
  dnsNames:
  - my-app.example.com
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer

When you apply this Certificate resource, cert-manager steps in. It looks at the issuerRef and finds a ClusterIssuer (or Issuer if scoped to a namespace) named letsencrypt-prod. This issuer is configured to communicate with a Certificate Authority (CA) like Let’s Encrypt.

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

The ClusterIssuer tells cert-manager how to obtain certificates. In this case, it’s configured to use the ACME protocol with Let’s Encrypt’s production server. The http01 solver indicates that it will solve the ACME challenge by serving a temporary file over HTTP on a publicly accessible endpoint. The ingress.class: nginx specifies that cert-manager should use an Ingress controller (here, Nginx) to expose the challenge endpoint.

Once the Certificate resource is created, cert-manager initiates the process. It generates a private key and a Certificate Signing Request (CSR). It then presents the CSR to the configured issuer (Let’s Encrypt). Let’s Encrypt responds with a challenge that cert-manager must solve to prove ownership of the domain my-app.example.com.

For the http01 challenge, cert-manager will create a temporary Ingress resource. This Ingress points to a cert-manager pod that serves the challenge token. The Ingress controller (Nginx) makes this endpoint accessible externally. Let’s Encrypt then probes this endpoint. If successful, Let’s Encrypt issues a signed certificate.

Finally, cert-manager receives the signed certificate and its corresponding private key, and stores them in a Kubernetes Secret named my-app-tls-secret in the default namespace. Your application deployment can then mount this secret as a volume and configure its web server (e.g., Nginx, Traefik) to use these TLS certificates for secure communication.

The true power lies in automation. When the certificate is nearing expiration (cert-manager defaults to 30 days before expiry), it automatically attempts to renew it by repeating the ACME challenge process. This eliminates the manual overhead of tracking expiration dates and performing renewals.

When you configure an ACME issuer, the privateKeySecretRef field is crucial. It specifies a Secret where cert-manager will store the ACME account’s private key. This key is used to authenticate with the CA across renewals and different certificate requests from the same account. If this secret is missing or corrupted, cert-manager cannot re-authenticate with the CA, and certificate issuance will fail.

The solvers section within an issuer defines how cert-manager attempts to prove domain ownership. You can configure multiple solvers, and cert-manager will try them in order until one succeeds. Common solvers include http01 (as seen above) and dns01. The dns01 solver is particularly useful for environments where direct HTTP access is difficult, as it involves creating TXT records in your DNS zone to prove domain control.

A common pitfall is misconfiguring the ingress.class or dnsProvider within the solver configuration. If cert-manager cannot correctly interact with your Ingress controller or DNS provider, the ACME challenges will fail, and certificates won’t be issued. For dns01 challenges, ensure the service account used by cert-manager has the necessary permissions to create and delete DNS records with your provider (e.g., AWS Route 53, Google Cloud DNS, Cloudflare).

Another subtle point is how cert-manager handles CertificateRequest resources. When a Certificate resource is created or needs renewal, cert-manager creates a CertificateRequest object. This object contains the CSR. The issuer then processes this CertificateRequest to obtain the certificate. If there are issues with the issuer configuration or the CA itself, you’ll often see errors related to CertificateRequest failures.

The commonName field in the Certificate resource is often overlooked. While dnsNames is used for Subject Alternative Names (SANs), the commonName is typically set to the first entry in dnsNames by default, and it’s a primary identifier for the certificate. If you omit dnsNames and only provide commonName, cert-manager will still function, but it’s generally best practice to use dnsNames for clarity and compatibility.

The next hurdle you’ll likely encounter is understanding how to integrate cert-manager with different Ingress controllers and service meshes for automatic certificate injection.

Want structured learning?

Take the full Tls-ssl course →