SSL: automatic renewal
Linux server — nginx, Apache, Caddy, HAProxy, Docker
A script for nginx, Apache, Caddy, HAProxy and Docker fetches the fresh certificate daily, checks it against your key and reloads the server with no downtime.
Renews by itself
Use this if you have SSH access to the server and root rights (a VPS, a dedicated server, your own machine in a rack). You need curl and openssl, which almost every Linux has.
What you need
- The certificate link from your account:
https://api.zrazu.com/ssl-pull/zsp_YOUR_TOKEN. - The private key the certificate was ordered with (a
key.pemfile or similar). If the CSR was created by the ZRAZU account, this is the file you saved back then. - 10 minutes.
Step 1. Put the key on the server
sudo mkdir -p /etc/ssl/zrazu/example.com
sudo chmod 700 /etc/ssl/zrazu/example.com
sudo nano /etc/ssl/zrazu/example.com/key.pem
sudo chmod 600 /etc/ssl/zrazu/example.com/key.pemIn nano, paste the whole key text, including the -----BEGIN … PRIVATE KEY----- and -----END … PRIVATE KEY----- lines, save (Ctrl+O, Enter) and exit (Ctrl+X). The key must have no password: if yours is password-protected, remove the password with openssl pkey -in old.key -out /etc/ssl/zrazu/example.com/key.pem.
Step 2. The renewal script
Create the file with sudo nano /usr/local/bin/zrazu-ssl-example.com.sh, paste the text below and change only the RELOAD line for your web server:
- nginx —
nginx -t && systemctl reload nginx(this is what is set); - Apache (Debian, Ubuntu) —
apachectl configtest && systemctl reload apache2; Apache (CentOS, Rocky, Alma) —apachectl configtest && systemctl reload httpd; - Caddy —
systemctl reload caddy; - HAProxy —
systemctl reload haproxyandCOMBINED=1(HAProxy reads the certificate and key from one file,combined.pem); - nginx in Docker —
docker exec CONTAINER_NAME nginx -s reload(mount the/etc/ssl/zrazudirectory into the container as a volume).
#!/bin/sh
# ZRAZU: updates the SSL certificate using the personal link. Run by cron once a day.
set -eu
# cron gives a short PATH without /usr/sbin, and nginx and apachectl live there.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PULL="https://api.zrazu.com/ssl-pull/zsp_YOUR_TOKEN"
DIR="/etc/ssl/zrazu/example.com"
KEY="$DIR/key.pem"
RELOAD="nginx -t && systemctl reload nginx"
COMBINED=0
umask 077
tmp=$(mktemp "$DIR/.new.XXXXXX")
trap 'rm -f "$tmp"' EXIT
curl -fsS --retry 3 --max-time 60 -o "$tmp" "$PULL/fullchain.pem"
grep -q "BEGIN CERTIFICATE" "$tmp" || { echo "ZRAZU: the link does not return a certificate"; exit 1; }
if [ -f "$DIR/.applied" ] && cmp -s "$tmp" "$DIR/.applied"; then
echo "ZRAZU: certificate unchanged"
exit 0
fi
cert_pub=$(openssl x509 -in "$tmp" -noout -pubkey | openssl sha256)
key_pub=$(openssl pkey -in "$KEY" -pubout | openssl sha256)
if [ "$cert_pub" != "$key_pub" ]; then
echo "ZRAZU: certificate was issued for a different key — changing nothing, put the new key in $KEY"
exit 1
fi
openssl x509 -in "$tmp" -noout -checkend 0 >/dev/null || { echo "ZRAZU: certificate has already expired"; exit 1; }
chmod 644 "$tmp"
mv -f "$tmp" "$DIR/fullchain.pem"
if [ "$COMBINED" = 1 ]; then
cat "$DIR/fullchain.pem" "$KEY" > "$DIR/combined.pem.new"
mv -f "$DIR/combined.pem.new" "$DIR/combined.pem"
fi
sh -c "$RELOAD"
cp "$DIR/fullchain.pem" "$DIR/.applied"
echo "ZRAZU: new certificate installed, valid until $(openssl x509 -in "$DIR/fullchain.pem" -noout -enddate | cut -d= -f2)"The script changes nothing if the web server already runs this certificate, if the file at the link is not a certificate, or if the certificate does not match your key or has already expired. It checks the web server configuration before reloading, so a broken config will not take your site down; and if the reload fails, the script tries again the next day.
Make the file executable and run it once by hand:
sudo chmod 700 /usr/local/bin/zrazu-ssl-example.com.sh
sudo /usr/local/bin/zrazu-ssl-example.com.shYou should see "new certificate installed" and the file /etc/ssl/zrazu/example.com/fullchain.pem should appear.
Step 3. Point the web server at the files (once)
nginx — in the site's server block:
ssl_certificate /etc/ssl/zrazu/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/zrazu/example.com/key.pem;Apache 2.4.8 and newer — in <VirtualHost *:443>:
SSLCertificateFile /etc/ssl/zrazu/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/zrazu/example.com/key.pemCaddy — in the site block: tls /etc/ssl/zrazu/example.com/fullchain.pem /etc/ssl/zrazu/example.com/key.pem. HAProxy — in bind :443 ssl crt /etc/ssl/zrazu/example.com/combined.pem.
Check the config (nginx -t or apachectl configtest) and reload the web server.
Step 4. Schedule
echo "17 4 * * * root /usr/local/bin/zrazu-ssl-example.com.sh >> /var/log/zrazu-ssl.log 2>&1" | sudo tee /etc/cron.d/zrazu-ssl-example.comThe script will run every day at 04:17 server time. You can pick any minute — just better not exactly 00.
Check
sudo tail /var/log/zrazu-ssl.log— tomorrow it will show "certificate unchanged": the schedule works.- The site serves our certificate:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -issuer -enddateor the page https://zrazu.com/tools/ssl/example.com/. - In your account, the certificate card shows that the server fetched the current certificate.
If it did not work
curl: (22) … 404— the link is disabled or replaced. Create a new one in the certificate card and replace thePULLline in the script.curl: (22) … 429— too many requests (over 60 per hour). Once a day is well within the limit.- "certificate was issued for a different key" — the certificate was reissued with a new key. Put the new key in
key.pemand run the script again. nginx: [emerg]after the reload — an error in the site configuration, not in the certificate; the old certificate stays in use until you fix the error.