Nginx and Let's Encrypt: free SSL with certbot on Ubuntu and Debian
To get a free SSL certificate for Nginx on a VPS, install certbot with its Nginx plugin and run certbot --nginx -d example.com -d www.example.com --redirect. Certbot obtains a Let's Encrypt certificate, adds it to the Nginx config and redirects HTTP to HTTPS; a systemd timer then renews it automatically. The steps below work on Ubuntu 24.04 and Debian 12.
What you need
- A VPS with Ubuntu 24.04 or Debian 12. Both are among the OS images you can pick when ordering or reinstall from your account. Nginx and certbot use very little memory, so a static site, a reverse proxy or an app with a small database runs fine on Pulse (2 vCPU, 2 GB RAM, $10/month).
- A domain pointed at the server. The A record for
example.comand thewwwrecord must already resolve to the server's IP, otherwise Let's Encrypt can't verify the domain. See how to connect a domain to a VPS. - Root access over SSH. See connecting over SSH.
1. Install Nginx
apt update && apt install -y nginx
If you use ufw, open HTTP and HTTPS. Port 80 must stay open: Let's Encrypt checks the domain over it on every renewal.
ufw allow 80/tcp
ufw allow 443/tcp
2. Create a server block
Create /etc/nginx/sites-available/example.com (replace example.com with your domain):
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Add a test page, enable the site and reload Nginx:
mkdir -p /var/www/example.com
echo "It works" > /var/www/example.com/index.html
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
curl -I http://example.com
curl should return HTTP/1.1 200 OK. The server_name line matters: certbot looks for it to know which block to edit.
3. Install certbot
apt install -y certbot python3-certbot-nginx
4. Get the certificate and enable the redirect
certbot --nginx -d example.com -d www.example.com --redirect
On the first run certbot asks for an email address and for acceptance of the Let's Encrypt terms. It then verifies the domain over HTTP, saves the certificate to /etc/letsencrypt/live/example.com/, adds listen 443 ssl to your server block and creates the HTTP to HTTPS redirect.
Check the result:
curl -I http://example.com
curl -I https://example.com
The first command should return 301 Moved Permanently with Location: https://example.com/, the second 200 OK.
5. Check automatic renewal
Let's Encrypt certificates are short-lived (90 days at most), so renewal must run on its own. The Debian and Ubuntu package installs a systemd timer that runs twice a day and renews certificates close to expiry. Make sure it's active and that renewal actually works:
systemctl list-timers certbot.timer
certbot renew --dry-run
certbot certificates
--dry-run goes through the full renewal against the Let's Encrypt test environment without replacing the certificate. After a real renewal the Nginx plugin reloads Nginx itself, so you don't need extra hooks.
Common mistakes
Timeout during connectorunauthorized. The domain doesn't point to this server yet, or port 80 is closed. Checkdig +short example.comand your firewall.Could not automatically find a matching server block.server_namedoesn't contain the domain you passed with-d. Fix the config, runnginx -t, and run certbot again.- The
wwwcheck fails. There's no DNS record forwww. Add it or drop-d www.example.com. - Too many failed attempts. Let's Encrypt limits failed validations per hour. Fix the cause before retrying, and test with
certbot renew --dry-runinstead of repeating the real request.