Issuing automatically with ACME

Free, automated certificates from Let’s Encrypt or ZeroSSL that renew themselves. This is the default answer unless OV/EV or an offline key requirement forces you elsewhere.

The renewal loop an ACME client runs on its own A timer wakes the ACME client twice a day. If the certificate has less than thirty days left the client asks the CA for a new one, proves control of the domain again, receives the certificate and reloads the web server with it. Then it goes back to sleep. Nothing in the loop requires a person. systemd timer or cron, twice a day ACME client certbot, acme.sh, caddy Let’s Encrypt challenge, then issue your web server reloaded with the new file wake up, check the expiry date fewer than 30 days left? new certificate, same automation reload, then sleep no step here waits for a person which is the entire point
The renewal you have to remember is the one that eventually gets forgotten. This loop runs whether or not anyone is watching — the only job left is checking that it is really running, which is step 4 below.
  1. Point DNS at the server first

    The CA validates by reaching your domain. An A/AAAA record must already resolve to the machine you're issuing on, and port 80 must be reachable for HTTP validation.

  2. Install an ACME client

    Certbot is the reference client; acme.sh is a dependency-free shell alternative.

    sudo apt install certbot python3-certbot-nginx # Debian/Ubuntu
    curl https://get.acme.sh | sh # acme.sh alternative
  3. Issue the certificate

    The plugin edits your web-server config, proves control, and installs the result in one command. No key or CSR handling on your part — the client generates both.

    sudo certbot --nginx -d example.com -d www.example.com
    sudo certbot --apache -d example.com -d www.example.com

    For a wildcard, HTTP validation isn't allowed — you must prove control via a DNS TXT record:

    sudo certbot certonly --manual --preferred-challenges dns -d '*.example.com' -d example.com
  4. Confirm renewal is automatic

    Packaged certbot installs a systemd timer or cron job already. Verify it rather than assuming — a silent renewal failure surfaces as an outage 90 days later.

    sudo certbot renew --dry-run && systemctl list-timers | grep certbot

Let's Encrypt's own documentation