Deploy a renewed certificate
Getting the certificate today
Section titled “Getting the certificate today”Three routes, in order of practicality:
From the Console. Open the certificate and download it. Fine for a handful of renewals, obviously not for a fleet.
From the CA directly. Your CA account has its own API and its own download endpoint. If you already hold credentials there, this is scriptable today.
From ACME, if the certificate is ACME-issued. An ACME client on the serving host does issuance and installation in one step, and the private key never moves. For Let’s Encrypt and similar, this is the path with the fewest moving parts - NextPKI then serves as the inventory and the approval gate rather than the delivery mechanism.
Pick the third where you can. It is the only one of the three that is both automated and keeps the key on the host.
The safe ordering
Section titled “The safe ordering”Whatever gets you the file, the installation order is what prevents self-inflicted outages:
- Generate the key and CSR on the serving host. Never move a private key between hosts.
- Write the new key beside the old one, not over it.
- Obtain the certificate.
- Verify the certificate matches the key before touching live configuration.
- Test the configuration.
- Reload, do not restart.
- Only then promote the new key over the old.
Step 4 is the one people skip. It is two commands:
# The moduli must match, or the server will not start with this pair.key_hash=$(openssl pkey -in server.key.new -pubout -outform der | openssl sha256)crt_hash=$(openssl x509 -in server.crt.new -pubkey -noout -outform pem \ | openssl pkey -pubin -pubout -outform der | openssl sha256)
[ "$key_hash" = "$crt_hash" ] || { echo "key and certificate do not match" >&2; exit 1; }Also confirm the chain is complete. A certificate that validates on your laptop and fails on a server is almost always a missing intermediate:
openssl verify -untrusted chain.pem server.crt.newinstall -m 640 -o root -g root server.crt.new /etc/ssl/certs/example.com.peminstall -m 600 -o root -g root server.key.new /etc/ssl/private/example.com.key
nginx -t && systemctl reload nginxnginx -t before reload is the whole safety net. Reload - not restart - keeps
existing connections and avoids a gap in service.
Concatenate the leaf and the intermediates into ssl_certificate; nginx does not
build the chain for you, and a bare leaf is the classic cause of “works in Chrome,
fails in Java”.
Apache
Section titled “Apache”SSLCertificateFile /etc/ssl/certs/example.com.pemSSLCertificateKeyFile /etc/ssl/private/example.com.keySSLCertificateChainFile /etc/ssl/certs/example.com.chain.pemapachectl configtest && systemctl reload apache2Traefik
Section titled “Traefik”Traefik watches its dynamic configuration, so replacing the files is enough - no reload command. Write to a temporary path and rename, so Traefik never observes a half-written file:
install -m 600 server.key.new /etc/traefik/certs/.example.com.key.tmpinstall -m 644 server.crt.new /etc/traefik/certs/.example.com.crt.tmpmv /etc/traefik/certs/.example.com.key.tmp /etc/traefik/certs/example.com.keymv /etc/traefik/certs/.example.com.crt.tmp /etc/traefik/certs/example.com.crtmv within a filesystem is atomic; cp is not. Copying directly over a file that
is being read is how you get a few seconds of TLS errors.
Kubernetes
Section titled “Kubernetes”Update the secret in place and let the ingress controller pick it up:
kubectl create secret tls example-com-tls \ --cert=server.crt.new --key=server.key.new \ --dry-run=client -o yaml | kubectl apply -f ---dry-run=client | apply is an update rather than a delete-and-recreate, so there
is no window where the secret does not exist.
Whether the controller reloads without help depends on the controller. ingress-nginx watches secrets and reloads by itself. Others need a rollout:
kubectl rollout restart deployment/my-appFor a fleet, the right answer is cert-manager with ACME rather than pushing secrets from outside - see below.
Verify from the outside
Section titled “Verify from the outside”Trust what a client sees, not what your file system says:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \ | openssl x509 -noout -subject -issuer -dates -fingerprint -sha256Compare the fingerprint with the one NextPKI reports for the new certificate. If they differ, something is still serving the old file - an unreloaded worker, a second node, or a CDN holding it.
Your sensors will observe the change on their next sweep, which closes the loop in the inventory. If a host still shows the old fingerprint a sweep later, that host did not get the update.
Where this is going
Section titled “Where this is going”An internal ACME server against the private CA is planned, and for internal certificates it removes this entire page: the client on the host requests, receives and installs, and the key never exists anywhere else. That is the shape worth designing towards.