# 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.

URL: https://zrazu.com/en/docs/ssl-auto-linux/

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.pem` file 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

```sh
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.pem
```

In `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 haproxy` and `COMBINED=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/zrazu` directory into the container as a volume).

```sh file=zrazu-ssl-update.sh
#!/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:

```sh
sudo chmod 700 /usr/local/bin/zrazu-ssl-example.com.sh
sudo /usr/local/bin/zrazu-ssl-example.com.sh
```

You 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:

```nginx
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>`:

```apache
SSLCertificateFile    /etc/ssl/zrazu/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/zrazu/example.com/key.pem
```

Caddy — 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

```sh
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.com
```

The script will run every day at 04:17 server time. You can pick any minute — just better not exactly 00.

## Check

1. `sudo tail /var/log/zrazu-ssl.log` — tomorrow it will show "certificate unchanged": the schedule works.
2. The site serves our certificate: `echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -issuer -enddate` or the page [https://zrazu.com/tools/ssl/example.com/](https://zrazu.com/tools/ssl/example.com/).
3. 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 the `PULL` line 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.pem` and 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.
