One of the challenges when running applications that require TLS in Kubernetes is handling certificate management safely and efficiently. If you are using cert-manager, you are already automating much of the certificate lifecycle. However, a common question I encounter is how to expose those certificates directly to a pod and ensure your application can seamlessly reload them as they are renewed. In this post, I want to walk through a practical approach using the nginx Docker image as an example.

Step 1: Setting Up cert-manager

Before we get into the specifics, you need to have cert-manager set up in your Kubernetes cluster. The cert-manager folks keep their installation instructions up to date and easy to follow. If you have not done so already, follow those steps to install cert-manager and confirm it is working in your cluster.

Step 2: Create a Certificate Resource

Once cert-manager is ready, create a Certificate resource for your domain. This resource tells cert-manager which certificates to manage, which issuer to use, and the Kubernetes secret where the certificate should be stored.

Here is an example manifest:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-com
  namespace: main
spec:
  dnsNames:
    - 'example.com'
  issuerRef:
    kind: ClusterIssuer
    name: cert-manager-acme-cluster-issuer
  secretName: example-com-tls

Make sure to apply this certificate resource in the same namespace as the pods that will use it.

Step 3: Add Volumes and Volume Mounts to Your nginx Deployment

To expose the certificate secrets to nginx, add a new volume to your Deployment spec that references the relevant secret, and mount it into the nginx pods.

Here is the snippet you can add to your deployment:

  volumes:
    - name: example-com-tls
      secret:
        secretName: example-com-tls
...
      volumeMounts:
        - mountPath: /etc/nginx/certs
          name: example-com-tls

This will mount the Kubernetes secret at /etc/nginx/certs in the container.

Step 4: Update nginx.conf to Use the Mounted Certificates

Update your nginx configuration to point to the certificates within the mounted directory. For a standard TLS server block, it may look like this:

ssl_certificate /etc/nginx/certs/tls.crt;
ssl_certificate_key /etc/nginx/certs/tls.key;

Reload nginx with this configuration so it knows where to find the certificate and key files.

Step 5: Automatically Reload nginx When Certificates Are Renewed

One last challenge is ensuring nginx reloads whenever Kubernetes rotates its certificate secrets. Unfortunately, tools like inotifywait do not work reliably with Kubernetes secrets, as the file update mechanism does not generate the expected inotify events.

My solution is a simple shell script that checks the certificate file’s checksum every minute. If the checksum changes, it triggers a reload of nginx. Here is the script you can use, which you should add as /docker-entrypoint.d/reload-nginx-on-cert-renewal.sh in your nginx Docker container:

#!/bin/bash

CERT_FILE="/etc/nginx/certs/tls.crt"

get_checksum() {
    sha256sum "${CERT_FILE}"
}

reload_on_renewal() {
    while true; do
        CURRENT_CHECKSUM="$(get_checksum)"

        while test "${CURRENT_CHECKSUM}" == "$(get_checksum)"; do
            sleep 86400 # Check once per day.
        done

        nginx -s reload
    done
}

reload_on_renewal &

Add execution permissions to the script and rebuild your nginx Docker image if necessary.

Wrapping Up

By combining cert-manager, Kubernetes secrets, and a simple file checksum watcher, you can automate the lifecycle of certificates and keep nginx serving fresh, valid TLS without manual intervention or downtime. This pattern is widely applicable, not just for nginx, but for any application that needs to react to underlying file changes in Kubernetes secrets.