DEV Community

Celso Nery
Celso Nery

Posted on

Kubernetes: Automating TLS Certificates with Cert-Manager

🇧🇷 Leia a versão em português aqui.

With the Ingress Controller already configured (see the previous article in this addons series), the cluster can route multiple HTTP domains to the correct Services. The natural next step is enabling HTTPS on those domains — and doing that manually, renewing certificates before they expire, doesn't scale well as the number of domains grows. That's exactly the problem cert-manager solves: it automates the issuance, renewal, and management of TLS certificates inside the cluster, integrating directly with certificate authorities like Let's Encrypt.

🔗 Official site: https://cert-manager.io/

Installing cert-manager

Installation is done by applying the project's official manifest, referencing the desired version:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.1/cert-manager.yaml
Enter fullscreen mode Exit fullscreen mode

Always check the cert-manager releases page to install the latest stable version — the link above references v1.16.1 as an example, but the project releases new versions frequently, and it's worth using one compatible with your Kubernetes version.

This manifest creates cert-manager's components (controller, webhook, and cainjector) in a dedicated namespace (cert-manager), plus the Custom Resource Definitions (CRDs) needed — such as ClusterIssuer, Certificate, and CertificateRequest — which become new object types in the cluster.

Creating a ClusterIssuer

cert-manager, on its own, doesn't know where to issue certificates from — you need to configure an Issuer (scoped to a namespace) or a ClusterIssuer (available cluster-wide), pointing to a certificate authority.

A common example, using Let's Encrypt with HTTP-01 validation (which depends on the Ingress Controller already being in place, since validation happens through an HTTP request to the domain):

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@bagarote.com.br
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx
Enter fullscreen mode Exit fullscreen mode

Where:

  • server: Let's Encrypt's production API address (a staging environment also exists, recommended for testing, avoiding hitting the production environment's certificate issuance rate limits);
  • email: used by Let's Encrypt for notifications about expiration or issues with certificates;
  • solvers.http01.ingress.ingressClassName: indicates that validation will be done through the already-installed nginx Ingress Controller.

Applying the ClusterIssuer:

kubectl apply -f clusterissuer.yaml
Enter fullscreen mode Exit fullscreen mode

To confirm it was created correctly:

kubectl get clusterissuer
Enter fullscreen mode Exit fullscreen mode

Enabling HTTPS on the application's Ingress

With the ClusterIssuer created, just annotate the application's Ingress so cert-manager automatically issues and manages that domain's certificate:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ecommerce-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
    - hosts:
        - ecommerce.bagarote.com.br
      secretName: ecommerce-tls
  rules:
    - host: ecommerce.bagarote.com.br
      http:
        paths:
          - pathType: "Prefix"
            path: "/"
            backend:
              service:
                name: ecommerce
                port:
                  number: 80
Enter fullscreen mode Exit fullscreen mode

The cert-manager.io/cluster-issuer annotation tells cert-manager which ClusterIssuer to use for that domain, and the tls block defines which Secret (ecommerce-tls, in the example) the issued certificate should be stored in.

Tracking certificate issuance

After applying the Ingress, cert-manager automatically starts the process of requesting and validating the certificate. You can track each stage through a few commands:

Check whether the Secret with the certificate has already been created:

kubectl get secrets
Enter fullscreen mode Exit fullscreen mode

Check the status of the Certificate object (automatically created by cert-manager from the Ingress annotation):

kubectl get certificate
kubectl describe certificate ecommerce-tls
Enter fullscreen mode Exit fullscreen mode

If the certificate doesn't become Ready quickly, the next level of detail is the CertificateRequest — the actual issuance request:

kubectl describe certificaterequest ecommerce-tls-2ld57
Enter fullscreen mode Exit fullscreen mode

And, going even deeper, the Challenge — which represents the domain validation step with Let's Encrypt (this is usually where DNS or Ingress routing issues show up):

kubectl describe challenges --all-namespaces
Enter fullscreen mode Exit fullscreen mode

Finally, to view all the Custom Resource Definitions installed by cert-manager (useful for understanding the extent of what was added to the cluster):

kubectl get crd
Enter fullscreen mode Exit fullscreen mode

Final thoughts

With cert-manager configured, new domains get TLS certificates issued and renewed automatically, just by adding the correct annotation to the respective Ingress — with no future manual intervention needed. When something doesn't work as expected, the Certificate → CertificateRequest → Challenge sequence is the natural investigation path, since each object represents a more granular step of the issuance process.

Top comments (0)