Find the process that consumes the files
ACME clients obtain certificates; application daemons decide when to read them. Some servers manage ACME and reload internally. Others read paths supplied by a separate client. Do not add a deployment hook until you know which process owns the active TLS socket and which files it opens.
sudo ss -H -lntp 'sport = :443'
systemctl cat example.service
sudo lsof -p "$(systemctl show -p MainPID --value example.service)" \
| grep -E '\.(pem|crt|key)$'Symlinked certificate directories can change targets during renewal. Check the final resolved paths, ownership, and mode rather than only the top-level link.
Validate the certificate and key as a pair
Inspect names, issuer, validity, and public-key identity before changing the service. Hashing the public keys is a simple format-independent match test.
openssl x509 -in fullchain.pem -noout \
-subject -issuer -dates -ext subjectAltName
openssl x509 -in fullchain.pem -noout -pubkey | sha256sum
openssl pkey -in privkey.pem -pubout | sha256sumThe two public-key hashes must match. The private key should be readable by the service through the narrowest practical group or credential mechanism, and never be world-readable.
namei -l /etc/example/tls/privkey.pem
stat -c '%U %G %a %n' /etc/example/tls/privkey.pemMake the deployment hook narrow
A deployment hook runs only after a successful renewal. It should validate application configuration, reload one service, and fail loudly. Avoid a restart when the daemon supports a reliable reload.
example-daemon validate --config /etc/example/config
systemctl reload example.service
systemctl is-active --quiet example.service
journalctl -u example.service --since '-5 minutes' -p warning --no-pagerA reload request returning zero only confirms that the manager delivered it. Review logs and the live socket. If the daemon keeps the old certificate after reload, document the required restart and schedule it rather than hiding downtime inside an automatic hook.
Inspect the certificate actually served
Read the public endpoint with the correct SNI. Compare its serial number and dates with the candidate on disk.
openssl s_client \
-connect service.example:443 \
-servername service.example </dev/null 2>/dev/null \
| openssl x509 -noout -serial -subject -issuer -datesThen make a normal HTTPS request from outside the host. This catches routing, hostname, chain, and trust problems that a local file inspection cannot.
curl --fail --show-error --max-time 15 \
https://service.example/Test the renewal client with its supported dry-run mode, but remember that staging issuance is not proof that the production hook, permissions, and public endpoint all work together.
Renewal checklist
- Identify the active TLS process and its resolved file paths.
- Check SANs, issuer, dates, and the certificate-key pair.
- Keep private-key permissions narrow.
- Validate application configuration before reload.
- Review service logs and the live socket afterward.
- Compare the served serial and dates through correct SNI.
- Run a real external HTTPS request and monitor days to expiry.