Intro
A TLS certificate failure can take a production service offline in seconds. The cause might be an expired leaf certificate, a missing intermediate, a revoked chain, or a botched renewal that replaced a valid certificate with an unreadable file. Recovery depends on whether you have a recent backup, a tested restore procedure, and a way to prove the service is healthy before you declare the incident over.
This article provides a practical workflow for backing up, restoring, validating, and rolling back TLS certificates. It is written for developers, DevOps engineers, and technical startup teams who manage certificates on Linux servers, Nginx, and Kubernetes Ingress. Every step includes concrete commands, expected output, and failure signals. The goal is operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify the result, and document how to recover if the expected state is not reached.
If you copy a command without checking prerequisites and expected output, you are not following an operations procedure. Each command in this article is scoped to a component, and each intervention includes a verification step. Where a rollback is possible, the procedure is described before the change is made.
Version and Environment Inventory
Before you back up or restore any certificate, you need to know what you are working with. Inventory the certificate files, the services that use them, the operating system and OpenSSL version, and the current validity window. This section shows how to collect that information without modifying anything.
Start by identifying the certificate files on disk. On a typical Debian or Ubuntu server, certificates are stored under /etc/ssl/certs/ and private keys under /etc/ssl/private/. On CentOS or RHEL, the paths are /etc/pki/tls/certs/ and /etc/pki/tls/private/. Nginx usually references certificates from /etc/nginx/ssl/ or a similar custom directory. If you are using Kubernetes, certificates are often stored as TLS Secrets in the cluster, not as files on the node.
To find all certificate files on a Linux host, you can use the find command to list files ending in .pem or .crt:
find /etc/ssl /etc/nginx /etc/pki -type f \( -name "*.pem" -o -name "*.crt" \) 2>/dev/null
This command may return a long list. Narrow it down by checking which certificate is actually used by a running service. For Nginx, inspect the configuration:
nginx -T 2>/dev/null | grep -E "ssl_certificate|ssl_certificate_key"
Example output:
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
Now you know the exact certificate and key path. Record the OpenSSL version to ensure compatibility with your commands:
openssl version
Example output:
OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
Next, inspect the certificate without changing anything. The following command prints the subject, issuer, serial number, validity dates, and SHA-256 fingerprint:
openssl x509 -in /etc/nginx/ssl/example.com.pem -noout -subject -issuer -serial -dates -fingerprint -sha256
Example output:
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R3
serial=04F7E2A1B3C4D5E6F7A8B9C0D1E2F3A4B5C6
notBefore=Mar 1 12:00:00 2025 GMT
notAfter=May 30 12:00:00 2025 GMT
SHA256 Fingerprint=AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23
Record the fingerprint and validity dates in your inventory. You will need them later to verify that a restored certificate is the one you intended.
If the certificate is served by a remote endpoint, you can test it without touching the server:
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | openssl x509 -noout -subject -issuer -serial -dates -fingerprint -sha256
Example output is similar to the local inspection, but it reflects the live certificate. Compare the two fingerprints. If they match, the local file and the served certificate are the same. If they do not match, the running service is not using the file you inspected, perhaps because it has not been reloaded or because a different configuration block is active.
Kubernetes users can inspect a TLS Secret with kubectl:
kubectl get secret example-tls -n default -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -subject -issuer -serial -dates -fingerprint -sha256
The Secret name and namespace are examples; replace them with your own. The certificate data is base64-encoded in the Secret, so you must decode it before passing it to OpenSSL.
Finally, validate the certificate chain offline to ensure the leaf certificate is trusted by a CA bundle:
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt -untrusted /etc/nginx/ssl/intermediate.pem /etc/nginx/ssl/example.com.pem
Expected output if the chain is valid:
/etc/nginx/ssl/example.com.pem: OK
If the output shows an error such as "unable to get local issuer certificate" or "certificate has expired", the chain is broken or the certificate is expired. Fix these issues before proceeding with a backup; a backup of a broken certificate is not useful for recovery.
Safe Configuration Path
Backing up a TLS certificate is not just about copying files. It is about creating a recoverable snapshot that includes the certificate, the private key, and the exact configuration that uses them. The private key must be protected at rest and in transit. This section shows a safe path for creating and storing backups.
Assume you have identified the certificate at /etc/nginx/ssl/example.com.pem and the private key at /etc/nginx/ssl/example.com.key. Before making any changes, create a timestamped backup directory:
BACKUP_DIR="/var/backups/tls/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
Copy the certificate and key into the backup directory with restrictive permissions:
cp /etc/nginx/ssl/example.com.pem "$BACKUP_DIR/"
cp /etc/nginx/ssl/example.com.key "$BACKUP_DIR/"
chmod 600 "$BACKUP_DIR/example.com.key"
chmod 644 "$BACKUP_DIR/example.com.pem"
The private key should never be world-readable. The certificate itself is public information and can have looser permissions.
Additionally, back up the Nginx configuration that references these files, because a restore may need to revert configuration changes too:
cp /etc/nginx/sites-available/example.com.conf "$BACKUP_DIR/"
If you use a configuration management tool like Ansible, Puppet, or Chef, the configuration is already versioned in your repository. In that case, record the commit hash or version.
Now create a manifest that records the backup contents and the certificate metadata for verification later:
{
echo "Backup created: $(date)"
echo "Host: $(hostname)"
echo "Certificate file: example.com.pem"
echo "Key file: example.com.key"
echo "Config file: example.com.conf"
openssl x509 -in "$BACKUP_DIR/example.com.pem" -noout -subject -issuer -serial -dates -fingerprint -sha256
} > "$BACKUP_DIR/MANIFEST.txt"
Example MANIFEST.txt:
Backup created: Mon Mar 10 10:00:00 UTC 2025
Host: web1
Certificate file: example.com.pem
Key file: example.com.key
Config file: example.com.conf
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R3
serial=04F7E2A1B3C4D5E6F7A8B9C0D1E2F3A4B5C6
notBefore=Mar 1 12:00:00 2025 GMT
notAfter=May 30 12:00:00 2025 GMT
SHA256 Fingerprint=AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23
Store the backup securely. If possible, encrypt the private key backup with a passphrase using GPG or OpenSSL. For example, to encrypt the key file with OpenSSL using AES-256-CBC:
openssl enc -aes-256-cbc -salt -in "$BACKUP_DIR/example.com.key" -out "$BACKUP_DIR/example.com.key.enc"
You will be prompted for a passphrase. Store the encrypted file off-server, such as in a secure object storage bucket or a secrets manager. The original key file on the server remains unchanged; you can optionally remove the unencrypted copy from the backup directory after encryption.
For Kubernetes, backup a TLS Secret with kubectl to a YAML file:
kubectl get secret example-tls -n default -o yaml > "$BACKUP_DIR/example-tls.yaml"
This YAML contains the base64-encoded certificate and key. Treat it as sensitive. Encrypt it before storing off-cluster.
Test the backup by restoring it to a temporary location and comparing the certificate fingerprint with the manifest:
mkdir -p /tmp/tls-restore-test
cp "$BACKUP_DIR/example.com.pem" /tmp/tls-restore-test/
cp "$BACKUP_DIR/example.com.key" /tmp/tls-restore-test/
openssl x509 -in /tmp/tls-restore-test/example.com.pem -noout -fingerprint -sha256 | diff - <(grep "SHA256 Fingerprint" "$BACKUP_DIR/MANIFEST.txt")
If diff outputs nothing, the backup is intact. Remove the test directory afterwards.
Verification and Diagnostics
After restoring a certificate, you must verify that the service is serving the correct certificate and that clients can validate the chain. This section provides a sequence of checks that move from local file inspection to remote endpoint testing and finally to automated monitoring.
First, compare the fingerprint of the restored certificate with the expected fingerprint from your backup manifest:
openssl x509 -in /etc/nginx/ssl/example.com.pem -noout -fingerprint -sha256
Expected output should match the fingerprint recorded in MANIFEST.txt. If it does not, you restored the wrong file or the file was corrupted.
Next, check the validity dates:
openssl x509 -in /etc/nginx/ssl/example.com.pem -noout -dates
Ensure notBefore is in the past and notAfter is in the future. If the certificate is expired or not yet valid, Nginx will fail to start or serve errors.
Test the Nginx configuration for syntax errors before reloading:
nginx -t
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
If there is an error, Nginx will not reload. Fix the configuration before proceeding.
Reload Nginx to apply the restored certificate:
systemctl reload nginx
Now test the live endpoint. Use openssl s_client to connect and display the served certificate:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -fingerprint -sha256
Compare the fingerprint with the expected one. If the handshake fails, s_client will show an error such as "unable to get local issuer certificate" or "certificate verify failed".
To check the chain as seen by clients, use the -showcerts option and count the number of certificates returned:
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | grep -c "BEGIN CERTIFICATE"
Typically, this returns 2 or 3: the leaf, one or more intermediates. If it returns only 1, the server is not sending the intermediate, which causes trust errors for some clients.
For a more thorough check, use the openssl verify command with the CA bundle:
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{print}' > /tmp/chain.pem
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt -untrusted /tmp/chain.pem /tmp/chain.pem
This is not always accurate because s_client output may include extra certificates. A better approach is to use a dedicated tool like testssl.sh or a web-based SSL checker for an external view.
Set up automated monitoring to catch certificate expiry before it causes downtime. A simple cron job can check the remaining validity days and send an alert:
#!/bin/bash
CERT=/etc/nginx/ssl/example.com.pem
DAYS_LEFT=$(openssl x509 -in "$CERT" -checkend $((30*24*3600)) -noout && echo yes || echo no)
if [ "$DAYS_LEFT" = "no" ]; then
echo "Certificate $CERT expires in less than 30 days" | mail -s "TLS expiry warning" [email protected]
fi
Save this script as /usr/local/bin/check_cert.sh, make it executable, and add a cron entry:
0 8 * * * /usr/local/bin/check_cert.sh
This runs every day at 8 AM server time.
Failure Modes and Recovery
Certificate failures have several distinct modes. Each requires a specific recovery procedure. This section describes the most common failures, how to detect them, and how to recover using the backups you created earlier.
Expired certificate: The notAfter date has passed. Clients see "ERR_CERT_DATE_INVALID" in browsers or connection failures in applications. Detection: run the date check described above. Recovery: replace the certificate with a valid one. If you have a recent backup that is still valid, restore it. Otherwise, obtain a new certificate from your CA or renew with Let's Encrypt. After replacement, reload the service and verify the live certificate dates.
Certificate chain incomplete: The server does not send the intermediate certificate. Browsers may work if they cache intermediates, but other clients fail with "unable to get local issuer certificate". Detection: s_client -showcerts returns only one certificate when two or more are expected. Recovery: obtain the intermediate certificate from your CA and configure the server to send it. For Nginx, concatenate the leaf and intermediate into a single file, with the leaf first:
cat example.com.pem intermediate.pem > fullchain.pem
Then update the ssl_certificate directive to point to fullchain.pem and reload Nginx. Verify with s_client -showcerts.
Private key mismatch: The certificate and key do not correspond. Nginx fails to start or reload, logging an error like "SSL_CTX_use_PrivateKey_file: key values mismatch". Detection: compare the modulus of the certificate and key:
openssl x509 -in example.com.pem -noout -modulus | openssl md5
openssl rsa -in example.com.key -noout -modulus | openssl md5
If the MD5 hashes differ, the key does not match the certificate. Recovery: restore the correct key from backup, or create a new key and obtain a new certificate.
Corrupted files: The certificate or key file is unreadable or contains invalid data. Detection: openssl commands fail with parse errors. Recovery: restore from backup and verify the fingerprint.
Accidental deletion: The certificate, key, or configuration file is removed. Detection: service fails to start, or the file is missing. Recovery: restore from backup.
Misconfigured permissions: The private key is too permissive (e.g., world-readable). Detection: ls -l shows permissions like -rw-r--r--. Recovery: set permissions to 600 and ownership to root:
chmod 600 /etc/nginx/ssl/example.com.key
chown root:root /etc/nginx/ssl/example.com.key
Revoked certificate: The CA has revoked the certificate. Clients see errors depending on revocation checking. Detection: use openssl verify with a CRL or OCSP. Recovery: obtain a new certificate.
For each failure, the rollback procedure is to restore the previous known-good certificate and key from backup, reload the service, and verify the live endpoint. Always test the rollback procedure in a staging environment before you need it in production.
Operations Checklist
Use this checklist before, during, and after any certificate change. It summarizes the key steps and assigns a single owner for each decision or action. Owners are illustrative roles; replace them with the actual person on your team. Each item should be reviewed at least monthly, or immediately after any incident.
| Step | Action | Owner (example) | Review frequency |
|---|---|---|---|
| 1 | Inventory all certificates: paths, subjects, issuers, expiry dates | Priya Shah, Engineering Lead | Monthly |
| 2 | Verify certificate chain validity offline with openssl verify | Tom Nguyen, DevOps Engineer | Monthly |
| 3 | Create timestamped backup of certificate, key, and config | Tom Nguyen, DevOps Engineer | Before every change |
| 4 | Encrypt private key backup and store off-server | Maria Garcia, Security Engineer | Quarterly |
| 5 | Test restore to a temporary location and compare fingerprint | Tom Nguyen, DevOps Engineer | Monthly |
| 6 | After restore, test Nginx config with nginx -t | Tom Nguyen, DevOps Engineer | On every restore |
| 7 | Reload Nginx and verify live certificate with s_client | Tom Nguyen, DevOps Engineer | On every restore |
| 8 | Set up automated expiry monitoring with cron or a monitoring tool | Tom Nguyen, DevOps Engineer | Quarterly |
| 9 | Document rollback procedure and test in staging | Priya Shah, Engineering Lead | Quarterly |
| 10 | Review certificate renewal process and automate where possible | Priya Shah, Engineering Lead | Annually |
For Kubernetes, adapt the checklist:
- Backup TLS Secrets with kubectl get secret -o yaml and encrypt the output.
- Before applying a new Secret, validate the certificate and key locally.
- After applying, verify the Ingress serves the correct certificate using openssl s_client against the Ingress endpoint.
- Use cert-manager or a similar tool to automate issuance and renewal, and monitor certificate expiry with Prometheus alerts.
Common Pitfalls and How to Avoid Them
Many certificate incidents are caused by avoidable mistakes. Here are the most common pitfalls in TLS backup and restore, why they happen, and how to prevent or recover from them.
Pitfall 1: Backing up only the certificate, not the private key. A certificate is useless without its matching key. This happens when operators assume the key is static or stored elsewhere. How to avoid: always back up the key with the certificate, and verify they match by comparing modulus MD5 hashes. Recovery: if the key is lost, you must generate a new key and obtain a new certificate.
Pitfall 2: Not testing backups. A backup that cannot be restored is not a backup. Operators often create backups but never practice restore. How to avoid: schedule a monthly restore test to a temporary location. Recovery: if the backup is found to be corrupt during an incident, there is no remedy; you must obtain a new certificate immediately.
Pitfall 3: Overwriting a valid certificate with an invalid one. This happens during automated renewal when a script replaces files without checking validity. How to avoid: validate the new certificate before replacing the old one, and keep the old certificate until the new one is confirmed working. Recovery: restore the old certificate from backup.
Pitfall 4: Not sending the full chain. Some servers are configured with only the leaf certificate, causing trust errors for some clients. This happens when the intermediate is not concatenated. How to avoid: use a fullchain.pem file that contains leaf and intermediates. Recovery: create the fullchain and update the configuration.
Pitfall 5: Ignoring file permissions. Private keys with loose permissions can be read by other users, leading to compromise. This happens when copying files without setting permissions. How to avoid: always chmod 600 on private keys. Recovery: change permissions immediately and consider the key compromised; rotate if necessary.
Pitfall 6: Not monitoring expiry. Manual tracking of certificate expiry is unreliable. This happens when there is no automated alerting. How to avoid: set up a cron job or monitoring system to check expiry and alert at 30, 14, and 7 days before expiration. Recovery: if it expires, restore from backup or obtain a new certificate urgently.
Pitfall 7: Not documenting the rollback procedure. In an incident, you may not have time to figure out how to revert. This happens when changes are made without a written rollback plan. How to avoid: write a step-by-step rollback procedure before any change, and test it in staging. Recovery: if no procedure exists, use the generic steps in this article: restore from backup, reload, verify.
Conclusion
TLS certificate backup and restore is a critical operational skill. By following the practices in this article, you can reduce the risk of certificate-related outages and recover quickly when they happen. The key points are:
- Inventory your certificates and record their fingerprints and validity dates.
- Back up the certificate, key, and configuration together, and encrypt the key backup.
- Test your backups by restoring to a temporary location and comparing fingerprints.
- After any restore, verify the configuration, reload the service, and test the live endpoint.
- Be prepared for common failure modes: expiry, chain issues, key mismatch, corruption, deletion, and permissions.
- Avoid pitfalls by testing backups, validating before changes, and monitoring expiry.
- Assign a single owner for each operational step and review the process regularly.
As a next step, choose one low-risk verification for your TLS certificates. Record the current state, run the documented check, compare the result with the expected signal, and review dependencies such as Nginx, Kubernetes Ingress, and Linux. A reliable technical workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision.