How to get a free SSL certificate with Certbot and Nginx on Ubuntu 24

Certbot with Nginx is the usual way to put a free Let’s Encrypt SSL certificate on an Ubuntu server: Certbot gets the certificate, writes the HTTPS lines into your site’s config and renews it on schedule. This guide follows the instructions on certbot.eff.org for Ubuntu 24.04 and 22.04. It also lists the errors people run into most and what each one means.

What you need before you start

  • A domain whose A record (and AAAA record, if it has one) points to this server. Let’s Encrypt checks the domain from the internet.
  • Nginx with a site whose server_name names that domain. If you don’t have one yet, see how to install Nginx on Ubuntu.
  • Ports 80 and 443 open. Let’s Encrypt connects on port 80 to check that the domain is yours.

If UFW is on (its status says Status: active), allow Nginx on both ports:

sudo ufw allow 'Nginx Full' sudo ufw status

On a cloud server, open ports 80 and 443 in the provider’s firewall or security group too. UFW doesn’t control that one.

Install Certbot with snap

certbot.eff.org recommends the snap package: it is always the current version and renews certificates on its own. Ubuntu has snap built in. The first command removes an older Certbot installed from apt, if there is one. Your certificates in /etc/letsencrypt/ stay where they are.

sudo apt remove certbot sudo snap install --classic certbot sudo ln -s /snap/bin/certbot /usr/local/bin/certbot certbot --version

The link puts the certbot command on the usual path. If apt says Package 'certbot' is not installed, so not removed, there was nothing to remove.

Run Certbot with Nginx

Name every domain the certificate should cover with its own -d. On the first run Certbot asks for an email address and for you to accept the Let’s Encrypt terms.

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

Certbot finds the server block with that server_name, adds the certificate lines and a redirect from HTTP to HTTPS, and reloads Nginx. At the end it reports Successfully received certificate and Successfully deployed certificate. Open https://example.com: the browser shows the padlock.

If you’d rather edit the Nginx config yourself, run this instead. Certbot then only gets the certificate and leaves your files alone:

sudo certbot certonly --nginx

Check that the certificate renews

Let’s Encrypt certificates last 90 days, and Let’s Encrypt plans to shorten that to 45 days over the next years. The snap package installs a systemd timer that runs certbot twice a day and renews any certificate close to expiry. Run a test renewal once and see the timer:

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

The test should end with Congratulations, all simulated renewals succeeded. It talks to the Let’s Encrypt test server and changes nothing.

Since June 2025 Let’s Encrypt no longer emails reminders before a certificate expires. Now and then check each certificate’s expiry date with: sudo certbot certificates

Where Certbot keeps its files

  • /etc/letsencrypt/live/example.com/: the current certificate. Nginx reads fullchain.pem and privkey.pem from here.
  • /etc/letsencrypt/renewal/example.com.conf: how this certificate renews.
  • /var/log/letsencrypt/letsencrypt.log: the full log of every run.
  • All certificates, their domains and expiry dates: sudo certbot certificates

Keep privkey.pem private: anyone who has it can pretend to be your site. Don’t copy it to other servers or into a Git repository.

If Certbot fails

  • Could not automatically find a matching server block for example.com. Set the server_name directive to use the Nginx installer.: the certificate was issued, but no site has that name. Add server_name example.com; to your site and check the config: sudo nginx -t Reload Nginx, then install the saved certificate: sudo certbot install --cert-name example.com
  • Timeout during connect (likely firewall problem): Let’s Encrypt couldn’t reach port 80. Check UFW and the cloud provider’s firewall, and that Nginx is running.
  • DNS problem: NXDOMAIN looking up A for example.com: the domain has no A record yet, or the new record hasn’t spread. Check it at your DNS provider and try again later. A stale AAAA record pointing elsewhere causes failures too.
  • too many certificates with already issued for this exact set of identifiers: Let’s Encrypt issues at most 5 certificates for the same set of names in 7 days. Wait, and test changes with --dry-run, which doesn’t count.
  • For any other error, read the end of /var/log/letsencrypt/letsencrypt.log.

If you also work on a Mac

MacWashing, System Junk: Xcode junk, user caches, installers and logs found, 22.83 GB ready to be cleaned

System Junk in MacWashing finds the caches of every app it knows, including those hidden inside app data, plus logs, Xcode junk and leftovers of deleted apps. It shows how much each takes and clears them in one go. Anything debatable stays unchecked until you decide.

Free trial · macOS 14 or later

Questions and answers

How do I use Certbot with Nginx?

Install Certbot: sudo snap install --classic certbot Then run: sudo certbot --nginx -d example.com It gets the certificate, edits your Nginx site and sets up renewal.

Is a Let’s Encrypt certificate really free?

Yes. Let’s Encrypt is a nonprofit certificate authority and doesn’t charge for certificates. Browsers trust them the same way as paid ones.

Do I need to renew the certificate by hand?

No. The Certbot timer renews it before it expires and reloads Nginx. Run this once to make sure renewal works: sudo certbot renew --dry-run