Skip to main content

Certbot and Let's Encrypt SSL

Let's Encrypt provides free TLS certificates, and Certbot automates issuing and renewing them. The main thing to get right is the ACME validation path (usually HTTP on port 80) and making sure your web server reloads after renewal.

Quick Summary
  • Make sure DNS points to your VPS and ports 80/tcp and 443/tcp are reachable.
  • Prefer a web-server plugin (--nginx/--apache) when available; use --webroot for OpenLiteSpeed.
  • Verify the certificate live with curl -Iv and track renewals with systemctl list-timers.

Prerequisites

  • A domain name with A/AAAA records pointing to this VPS.
  • Firewall allows inbound 80/tcp and 443/tcp.
  • SSH access with sudo.
  • A working virtual host for your domain (even if it's HTTP-only initially).

Decide how Certbot should validate

Certbot must prove you control the domain. The two common methods are:

  • http-01 (most common): Certbot places a file under /.well-known/acme-challenge/ and Let's Encrypt fetches it over HTTP (port 80).
  • dns-01: uses DNS TXT records (useful if you cannot expose port 80).

For most VPS WordPress setups, use http-01.

Install Certbot

install-certbot-debian.sh
sudo apt update
sudo apt install -y certbot

Issue a certificate

Choose the section that matches your web server.

warning

If you use certbot --nginx or certbot --apache, Certbot may edit your server configuration. If this is a production host, keep a backup of your web server config first.

install-certbot-nginx-plugin.sh
sudo apt update
sudo apt install -y python3-certbot-nginx

issue-cert-nginx.sh
sudo certbot --nginx -d example.com -d www.example.com

test-nginx-config-and-reload.sh
sudo nginx -t
sudo systemctl reload nginx

Verify SSL is working

verify-with-curl.sh
curl -Iv https://example.com/

list-certbot-certs.sh
sudo certbot certificates

If you want to confirm the served certificate chain from the network:

openssl-sni-check.sh
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

Renewal

Most systems install a systemd timer for automatic renewals.

check-certbot-timer.sh
systemctl list-timers | rg -n 'certbot'

Always run a dry-run at least once:

certbot-renew-dry-run.sh
sudo certbot renew --dry-run

Reload your web server after renew

If your web server does not automatically reload, add a deploy hook.

renewal-hook-reload-webserver.sh
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-webserver.sh >/dev/null <<'SH'
#!/usr/bin/env bash
set -euo pipefail

if systemctl is-active --quiet nginx; then
systemctl reload nginx
elif systemctl is-active --quiet apache2; then
systemctl reload apache2
elif systemctl is-active --quiet lsws; then
systemctl reload lsws
fi
SH
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-webserver.sh

Troubleshooting

SymptomLikely causeFix
NXDOMAIN / validation fails immediatelyDNS not set or not propagatedVerify A/AAAA records and wait for propagation
Timeout connecting on port 80Firewall/security group blocks HTTPAllow 80/tcp and verify reachability
404 on challenge pathWrong --webroot or vhost mismatchConfirm the docroot that serves your domain
403 on challenge pathApp/WAF blocks /.well-knownAllow that path and retry
Rate limit errorsToo many requests in a short timeWait and reduce retries; test with --dry-run
Files and directories to know
  • Certificates: /etc/letsencrypt/live/<domain>/
  • Renew configs: /etc/letsencrypt/renewal/
  • Logs: /var/log/letsencrypt/