Practical guide · acme.sh + Nginx
acme.sh renewed the certificate, but Nginx still serves the old one
A successful renewal does not prove that your HTTPS service loaded the new certificate. With acme.sh, check the certificate record, the destination files saved by --install-cert, and the reload command before requesting another certificate.
This guide uses acme.sh with Nginx on Linux. The service example assumes systemd; adapt paths, service names and privileges to your deployment. Diagnostic commands use Bash, OpenSSL and GNU timeout. Run acme.sh under the account and with the configuration directories used by your existing installation. The local demonstration was tested with acme.sh 3.1.6 and Nginx 1.31.8.
1. Find the certificate record used for renewal
List the certificates, then inspect the relevant record. Replace example.com with the certificate’s main name. The examples with --ecc select an ECC certificate; omit that option from the commands for an RSA record. If you use custom --home, --config-home or --cert-home paths, retain them in each command rather than reading another installation.
"$HOME/.acme.sh/acme.sh" --list
"$HOME/.acme.sh/acme.sh" --info -d example.com --ecc Inspect Le_RealKeyPath, Le_RealFullChainPath and Le_ReloadCmd, along with issuance and renewal information. Keep this output private: configuration and hook commands can contain sensitive values. Check the renewal job’s account, schedule and logs. A failed issuance needs its own diagnosis; a valid new certificate with an installation error does not justify repeatedly forcing renewal.
2. Match the installation paths to Nginx
Use dedicated destination files for Nginx. acme.sh’s internal working directory is not a stable deployment interface. Check the paths in the active server configuration, including container mounts if applicable. This excerpt belongs inside an existing HTTPS server block.
ssl_certificate /etc/nginx/tls/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/tls/example.com/key.pem; Read the public certificate at the configured destination and record its SHA-256 fingerprint, validity dates and SANs. The full chain must contain the server certificate first, followed by its intermediates.
openssl x509 -in /etc/nginx/tls/example.com/fullchain.pem \
-noout -sha256 -fingerprint -issuer -dates -ext subjectAltName An updated internal acme.sh file does not prove that this destination was updated. An updated destination does not prove that Nginx loaded it. Keep the private key private and ensure that the process loading it has the required access.
3. Save the correct paths and reload command
The following command changes files immediately and reloads Nginx. Use it after checking the destination paths and the certificate record. Prepare the destination directory with suitable ownership and permissions. The account running acme.sh must be able to write those files and run the reload command unattended.
"$HOME/.acme.sh/acme.sh" --install-cert -d example.com --ecc \
--key-file /etc/nginx/tls/example.com/key.pem \
--fullchain-file /etc/nginx/tls/example.com/fullchain.pem \
--reloadcmd "/usr/sbin/nginx -t && /usr/bin/systemctl reload nginx" Adapt the absolute binary paths and systemd unit. The && prevents the reload when nginx -t fails. acme.sh saves these destinations and the reload command for later renewals. Existing destination file ownership and permissions are preserved. Check the exit status and logs: a reload failure can leave new files on disk while the old certificate remains in service. Correct the cause and rerun --install-cert with the intended parameters. In containers, the hook must reach the actual Nginx process rather than a host service with a similar name.
4. Verify the certificate received over TLS
From outside the hosting network, open a new TLS connection to a service you operate or are authorized to inspect. Preserve the hostname as SNI and compare the fingerprint with the destination file. This probe displays the certificate; it does not validate chain trust or hostname matching.
set -o pipefail
timeout 10 openssl s_client -connect example.com:443 \
-servername example.com </dev/null |
openssl x509 -noout -sha256 -fingerprint \
-issuer -dates -ext subjectAltName If the fingerprints differ, locate the component terminating TLS. A CDN or load balancer can serve its own certificate. Check each relevant public address and origin path; use IP:443 or [IPv6]:443 with -connect while retaining the hostname in -servername. Multiple replicas or RSA/ECDSA certificates can produce different observations. If the expected certificate is served but the browser rejects it, inspect the chain and covered names.
5. Reproduce a failed reload locally
The script runs the real acme.sh --install-cert command against temporary self-signed certificate fixtures. Nginx initially serves the old certificate. An intentionally failing reload command lets acme.sh copy the new files but returns an error. A second installation with a working reload command makes Nginx serve the new certificate.
Download and inspect the acme.sh demonstration
Requirements: Linux, Bash, a local Docker daemon, curl, OpenSSL and GNU coreutils. The script downloads a pinned acme.sh file, verifies its checksum and uses a pinned Nginx image. Read it, then run bash acme-sh-install-reload.sh.
BEFORE and SERVED show the old fingerprint; DISK shows the new one; AFTER must match DISK. Assertions also check the saved destination paths, reload command and preserved key-file permissions. The script uses a temporary acme.sh home and a loopback port, then removes its container and temporary files. It installs no cron job. The Docker image remains cached.
Only this version-pinned test seeds acme.sh’s internal state with synthetic files. Do not reproduce that setup in a real certificate store. The demonstration verifies installation, error handling and reloading, not ACME issuance, renewal scheduling or public browser trust.
6. Keep monitoring after the immediate check
Record the observed address, SNI, time and fingerprint after deployment. Confirm that the renewal job runs with the same paths and sufficient permissions when no interactive shell is available. Keep its failures visible through your existing job monitoring.
Nocert periodically observes certificates served by reachable TLS endpoints and evaluates expiration alert rules in a daily cycle. Check the endpoint and observation time, then configure alert recipients. This complements the immediate external probe. Nocert does not run acme.sh, install certificates or reload Nginx, and does not guarantee that every visitor receives the same certificate.