aethercert
Documentation

Replace a Kubernetes TLS Secret Certificate

Replace the certificate in a kubernetes.io/tls Secret: check who manages it (cert-manager, Helm, GitOps), update it in place or via a new Secret, restart workloads only if needed, verify.

Kubernetes applications usually take their certificate from a kubernetes.io/tls Secret with the keys tls.crt (server certificate followed by intermediates) and tls.key. An Ingress references it in spec.tls[].secretName, a Gateway in its listener's certificateRefs. Updating the Secret changes the stored data; the Ingress controller or workload decides when the new data becomes active.

Before you begin

First check who owns the Secret - a manual change is overwritten otherwise:

  • cert-manager: if a Certificate resource manages the Secret (kubectl get certificate -A), renew through cert-manager instead of editing the Secret.
  • Helm or GitOps (Argo CD, Flux): the source of truth is the chart values or the Git repository - often encrypted with Sealed Secrets or SOPS, or synced by External Secrets. Change the certificate there and let it sync; a live change is reverted.
  • Otherwise you can update the Secret directly, as below.

Also note the context and namespace, the Secret name, which Ingress, Gateway or workloads use it, and whether it is immutable:

kubectl -n <namespace> get secret <secret> -o jsonpath='{.immutable}'

Back up the current certificate and key as files (not as YAML - a dumped Secret contains fields that make re-applying it fail):

kubectl -n <namespace> get secret <secret> -o jsonpath='{.data.tls\.crt}' | base64 -d > old-tls.crt
kubectl -n <namespace> get secret <secret> -o jsonpath='{.data.tls\.key}' | base64 -d > old-tls.key
chmod 600 old-tls.key

Step-by-step certificate replacement

  1. Check the new files - the key must belong to the certificate:

    openssl pkey -in private.key -pubout | sha256sum
    openssl x509 -in fullchain.pem -noout -pubkey | sha256sum
  2. Update the Secret in place:

    kubectl -n <namespace> create secret tls <secret> \
      --cert=fullchain.pem --key=private.key \
      --dry-run=client -o yaml | kubectl apply -f -

    This replaces only the data. Do not delete and re-create the Secret - that leaves a gap in which the Ingress has no certificate.

  3. Immutable Secret: it cannot be changed. Create a new one and point the Ingress (or Gateway) at it:

    kubectl -n <namespace> create secret tls <secret>-2026 --cert=fullchain.pem --key=private.key
    kubectl -n <namespace> patch ingress <ingress> --type=json \
      -p '[{"op":"replace","path":"/spec/tls/0/secretName","value":"<secret>-2026"}]'

    Delete the old Secret once the new one is verified.

  4. Activation:

    • Ingress controllers such as ingress-nginx and Traefik watch Secrets and use the new certificate without a restart.
    • A workload that mounts the Secret as a volume sees the new files after a short delay - except with a subPath mount, which never updates.
    • An application that reads the files only at start, or gets them through environment variables, needs a rollout:
    kubectl -n <namespace> rollout restart deployment/<deployment>
    kubectl -n <namespace> rollout status deployment/<deployment>

Verify the new certificate

Check the certificate in the Secret without printing the key, then the hostname:

kubectl -n <namespace> get secret <secret> -o jsonpath='{.data.tls\.crt}' |
  base64 -d | openssl x509 -noout -subject -issuer -enddate -fingerprint -sha256
openssl s_client -connect app.example.com:443 -servername app.example.com -showcerts </dev/null

The first command shows the first certificate in tls.crt, the server certificate. The served certificate must have the same fingerprint and the intermediates must be sent. Also check the controller's and Pods' events and logs.

Rollback

Apply the backed-up files the same way as step 2 (or point the Ingress back to the old Secret after step 3), or revert the change in Git or Helm. Repeat the activation step if the workload needed one.

Common problems

  • tls.crt contains only the server certificate, without intermediates.
  • The Secret was changed in the wrong context or namespace.
  • cert-manager, GitOps or Helm restored the old Secret.
  • The application reads the certificate only at start and no rollout happened.
  • A subPath mount never sees the update.
  • Broad RBAC lets unrelated users or workloads read the private key.

Automating certificate replacement with aethercert

The aethercert Kubernetes target creates or patches a kubernetes.io/tls Secret through the API and can trigger rolling restarts for explicitly listed Deployments in the same namespace.

See Deploy targets for the fields this preset takes, what it needs on the host, and how far it has been verified.

Sources

On this page