Paying $100 or more per year for a basic domain-validated SSL certificate has always felt like a tax on doing the web right. Let's Encrypt changed that in 2016 and today there is no legitimate reason to run a public website over plain HTTP. The nonprofit Certificate Authority issues free, browser-trusted 90-day certificates and the tooling around it — primarily Certbot — makes installation and automatic renewal something you can finish in under 15 minutes on most Linux servers. This guide covers the full process: obtaining the certificate, configuring your web server, setting up autorenewal, verifying everything works, and handling the most common errors people hit along the way.

How Let's Encrypt Works

Let's Encrypt uses the ACME protocol (RFC 8555) to automate the entire certificate lifecycle. When you run Certbot, it contacts the Let's Encrypt API, which then issues a challenge to prove you control the domain. There are two main challenge types:

  • HTTP-01: Let's Encrypt makes an HTTP request to http://yourdomain.com/.well-known/acme-challenge/TOKEN. Your server must respond with the correct value. This is the default and works for most setups.
  • DNS-01: You place a TXT record under _acme-challenge.yourdomain.com. This is required for wildcard certificates and works even when port 80 is blocked.

Once the challenge passes, Let's Encrypt signs your certificate and Certbot writes the key and cert files to /etc/letsencrypt/live/yourdomain.com/. The certificate is valid for 90 days, but Certbot's systemd timer or cron job renews it automatically when fewer than 30 days remain.

Prerequisites Before You Start

  • A domain name with DNS A (and AAAA if using IPv6) records pointing to your server's public IP.
  • Port 80 open in your firewall — Let's Encrypt must reach it for HTTP-01 challenges even if you plan to redirect everything to HTTPS afterward.
  • Root or sudo access on the server.
  • A supported OS: Ubuntu 20.04/22.04/24.04, Debian 10/11/12, CentOS/RHEL 8/9, or Rocky/AlmaLinux 8/9 are all well supported.
Before you start, confirm your domain's DNS is fully resolving to the correct server IP. Use the DNS Propagation Checker to verify your A record has propagated worldwide. If Let's Encrypt cannot resolve your domain during the challenge, the certificate request will fail.

Installing Certbot

The official and recommended way to install Certbot on any modern Linux distribution is via Snap. This ensures you always get the latest version regardless of your distro's package repository age.

sudo snap install --classic certbot sudo ln -s /snap/bin/certbot /usr/bin/certbot

If Snap is not available (some minimal server images skip it), install it first:

# Ubuntu/Debian sudo apt update && sudo apt install snapd -y # RHEL/CentOS/Rocky sudo dnf install snapd -y sudo systemctl enable --now snapd.socket

Avoid installing Certbot from your distro's native package manager (apt/dnf) unless you are on a very recent release. Older repos ship outdated Certbot versions that can cause renewal failures.

Obtaining Your Certificate

Apache

Certbot's Apache plugin handles everything: it requests the certificate, edits your virtual host configuration to add the SSL directives, and sets up an HTTP-to-HTTPS redirect automatically.

sudo certbot --apache -d yourdomain.com -d www.yourdomain.com

Certbot will ask for an email address (for expiry notices) and ask you to agree to the Terms of Service. When prompted whether to redirect HTTP to HTTPS, choose option 2 (Redirect) unless you have a specific reason not to.

Nginx

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

The Nginx plugin edits your server blocks in place. Make sure your Nginx config already has a valid server_name directive matching the domains you are requesting, otherwise the plugin cannot locate the right block and will error out.

Standalone Mode (No Web Server Running)

If you are installing a certificate before setting up a web server, or the server is temporarily stopped, use standalone mode. Certbot spins up its own temporary HTTP server on port 80.

sudo systemctl stop nginx # or apache2 sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com sudo systemctl start nginx

Wildcard Certificates via DNS-01

Wildcard certs (*.yourdomain.com) require the DNS-01 challenge. This means you must add a TXT record to your DNS zone each time you request or renew the certificate. Many DNS providers have Certbot plugins for full automation (Cloudflare, Route 53, DigitalOcean, etc.). For a manual one-time request:

sudo certbot certonly --manual --preferred-challenges dns \ -d "*.yourdomain.com" -d yourdomain.com

Certbot will display the TXT record value you need to add at your DNS host. Add it, wait a minute or two for it to propagate, then press Enter. The wildcard cert covers every subdomain but does not cover the bare domain, which is why you should include both *.yourdomain.com and yourdomain.com in the request.

Where Your Certificate Files Live

After a successful issuance, Certbot creates symlinks under /etc/letsencrypt/live/yourdomain.com/:

  • fullchain.pem — Your certificate plus the Let's Encrypt intermediate chain. This is what you point your web server's ssl_certificate (Nginx) or SSLCertificateFile (Apache) directive at.
  • privkey.pem — Your private key. Point ssl_certificate_key or SSLCertificateKeyFile here. Never share this file.
  • chain.pem — The intermediate certificate only (rarely needed directly).
  • cert.pem — Your certificate only (without the chain). Do not use this alone; browsers need the full chain.

Verifying the Certificate is Working

Open your browser and navigate to https://yourdomain.com. Click the padlock and confirm the certificate issuer shows Let's Encrypt and the expiry is roughly 90 days out. For a command-line check:

echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \ | openssl x509 -noout -subject -issuer -dates

You should see issuer= /C=US/O=Let's Encrypt/CN=R10 (or R11/E5/E6 depending on the current active intermediate) and a notAfter date 90 days from today.

Use the DNS Lookup tool to confirm your domain resolves correctly from multiple locations — a mismatch between your DNS A record and your server's actual IP is one of the top reasons Let's Encrypt challenges fail.

Setting Up Automatic Renewal

When installed via Snap, Certbot automatically creates a systemd timer that runs twice daily and renews any certificate expiring within 30 days. Confirm it is active:

sudo systemctl status snap.certbot.renew.timer

To test the renewal process without actually renewing (a dry run):

sudo certbot renew --dry-run

If the dry run completes without errors, you are set. Certbot also reloads your web server after renewal so the new certificate is picked up without any downtime.

If you are on a system without systemd (older setups using cron), add a cron job manually:

sudo crontab -e # Add this line: 0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

Common Errors and How to Fix Them

Error: Connection Refused or Timeout During Challenge

Let's Encrypt could not reach port 80 on your server. Check your firewall:

# UFW sudo ufw allow 80/tcp sudo ufw allow 443/tcp # firewalld (RHEL/CentOS/Rocky) sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload # iptables sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT

Also check that your cloud provider's security group or network ACL allows inbound traffic on port 80. AWS Security Groups and GCP firewall rules are separate from the OS firewall and are a common blind spot.

Error: Too Many Certificates Already Issued for This Domain

Let's Encrypt enforces a limit of 5 duplicate certificates per week per registered domain. If you hit this, wait until the rate limit window resets (check https://crt.sh/?q=yourdomain.com to see recent issuances). During testing, always add the --staging flag to use Let's Encrypt's staging environment, which has much higher limits and issues certificates from an untrusted test CA:

sudo certbot --nginx --staging -d yourdomain.com -d www.yourdomain.com

Error: Nginx or Apache Plugin Cannot Find Server Block

The plugin searches your config for a server_name value that matches the domain. If your virtual host config is in a non-standard path or missing a server_name line, the plugin fails. Fix: ensure your vhost file under /etc/nginx/sites-enabled/ or /etc/apache2/sites-enabled/ contains the exact domain name you are requesting, then re-run Certbot.

Certificate Renewed but Browser Still Shows Old One

Your web server is still serving the old certificate from cache. Reload it:

sudo systemctl reload nginx # or sudo systemctl reload apache2

Renewal Fails Because Port 80 is Blocked (Production Server)

If your organization blocks port 80 at the network edge, switch to the DNS-01 challenge for renewals. Install the appropriate DNS plugin for your provider (example for Cloudflare):

sudo snap install certbot-dns-cloudflare # Create credentials file echo "dns_cloudflare_api_token = YOUR_API_TOKEN" > /etc/letsencrypt/cloudflare.ini chmod 600 /etc/letsencrypt/cloudflare.ini # Request certificate sudo certbot certonly --dns-cloudflare \ --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \ -d yourdomain.com -d "*.yourdomain.com"

Security Best Practices After Installation

Getting the certificate installed is only half the job. Make sure your SSL configuration is actually strong:

  • Disable TLS 1.0 and 1.1: Both are deprecated. In Nginx, set ssl_protocols TLSv1.2 TLSv1.3;. In Apache, use SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1.
  • Use strong cipher suites: The Mozilla SSL Configuration Generator at ssl-config.mozilla.org provides copy-paste-ready configs for Apache, Nginx, and HAProxy at modern, intermediate, and old compatibility levels.
  • Enable HSTS: Add Strict-Transport-Security: max-age=31536000; includeSubDomains to your response headers so browsers never attempt plain HTTP for your domain.
  • Monitor expiry: Even with autorenewal configured, set up an external monitor. Many free services (UptimeRobot, for example) can alert you if a certificate is about to expire, catching edge cases where renewal silently fails.

Migrating from a Paid Certificate

If you are replacing a commercial certificate, the steps are the same as a fresh install. Certbot writes its own paths into your web server config. After Certbot succeeds, remove or comment out any old SSLCertificateFile / SSLCertificateKeyFile lines pointing to your old certificate and confirm the new Let's Encrypt paths are in place before reloading the web server. Keep the old private key file archived somewhere safe for 30 days in case you need to roll back.

Keeping Things Running Long Term

The 90-day certificate lifetime is intentional — it limits the damage from a compromised key and encourages automation. The renewal timer fires twice a day but only acts when a certificate has fewer than 30 days left, so you have a wide window before anything breaks. Check renewal health monthly with sudo certbot renew --dry-run and review /var/log/letsencrypt/letsencrypt.log if you suspect problems. With Snap-based Certbot and a properly configured web server, the entire process is genuinely hands-off once it is set up correctly.