2 AM. Phone buzzing. Alert from monitoring: “SSL certificate expired — site returning ERR_CERT_DATE_INVALID”. A customer calls to report the browser is showing a bright red warning, and revenue is bleeding by the minute.
I ran into this situation for the first time two years ago on a production Ubuntu 20.04 server. Back then I had installed Let’s Encrypt manually with apt, forgot to set up auto-renew, and when the cert expired nobody noticed. That day I swore it would never happen again.
The Real Problem — Why Is My Site Showing “Not Secure”?
These three scenarios cover about 95% of the cases I’ve debugged:
- SSL was never installed: A freshly set up server running plain HTTP, Chrome shows “Not Secure” in the address bar, Google Search Console throws warnings.
- Certificate expired: Let’s Encrypt certs last 90 days. If auto-renew can’t run — firewall blocking port 80, DNS changes, dead cronjob — the certificate expires silently with no warning.
- Installed but web server isn’t loading it: Certbot ran successfully but Nginx/Apache config wasn’t reloaded properly, so the site is still serving HTTP.
Each scenario has a different root cause and fix — I’ll cover each one below.
Why Does Let’s Encrypt Fail?
Let’s Encrypt doesn’t issue certs to just anyone who asks. The ACME mechanism works like this: you say “I own example.com”, Let’s Encrypt’s server says “prove it” — by placing a special file on your web server or adding a TXT record to DNS. Once verified, it issues a 90-day certificate.
Fully automatic — when everything works. The problem is if anything in the chain gets blocked, the challenge fails and you don’t get a cert.
Most common causes:
- Port 80 blocked by UFW or cloud security groups (AWS/GCP/DigitalOcean)
- DNS A record not pointing to the server’s IP — common after server migrations, DNS needs 15–30 minutes to propagate
- Old certbot version from
apthas many bugs already fixed in the snap version - Web server holding port 80 while certbot is trying to use standalone mode
Three Ways to Install Certbot — Which Should You Choose?
Step 0: Check Before Doing Anything
Don’t install anything yet. Check these basics first:
# Check if DNS is pointing to the server
dig +short yourdomain.com
# Check if port 80 is open
sudo ufw status
curl -I http://yourdomain.com
# Check if certbot is already installed
certbot --version
If dig returns the correct server IP and curl can connect — you’re ready to go.
Method 1: Certbot via Snap (Recommended)
Snap is the officially recommended method by Let’s Encrypt since 2021. The reason is simple: it’s always the latest version, self-updating, and doesn’t conflict with the system.
One note before starting: Let’s Encrypt limits 5 duplicate certificates per domain per week. If you’re testing and failing repeatedly, add the --staging flag to avoid burning through your real quota.
# Remove old certbot if installed via apt (avoid conflicts)
sudo apt remove certbot
# Install snap certbot
sudo snap install --classic certbot
# Create symlink to use certbot command directly
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Get the certificate — certbot has dedicated plugins for Nginx and Apache that automatically update your config:
# For Nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# For Apache
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
# If no web server set up yet (standalone mode)
sudo certbot certonly --standalone -d yourdomain.com
During execution, certbot asks for your email (for expiry alerts) and requires you to agree to the ToS. Once done, it automatically updates the nginx config: adds an SSL block, configures cipher suites, and sets up HTTP → HTTPS redirect.
Method 2: Install via apt (Ubuntu 20.04 and Below)
If you don’t want to use snap, apt still works:
sudo apt update
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
Real-world downside: the version in Ubuntu’s apt repo tends to be a few months behind snap. I’ve personally hit bugs in certbot 1.12 from apt that were fixed long ago in certbot 2.x from snap.
Method 3: Wildcard Certificate with DNS Challenge
Have multiple subdomains (api.example.com, app.example.com, cdn.example.com…) and want a single certificate to cover all of them? Wildcard is the solution:
sudo certbot certonly \
--manual \
--preferred-challenges dns \
-d "*.yourdomain.com" \
-d yourdomain.com
DNS challenge requires adding a TXT record to DNS manually — no automation unless you have a DNS API. I only use this approach when I have a Cloudflare API key for automation; otherwise, I install certs per subdomain to keep things simple.
Production-Ready Setup — The Process I’ve Used Since 2024
After that 2am cert expiry incident, I thoroughly tested this process on a staging Ubuntu 22.04 server before rolling it out to production. Here’s everything you need to do:
1. Install and Obtain Certificate with Nginx
# Make sure Nginx is running
sudo systemctl status nginx
# Install certbot snap
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# Get the certificate — certbot automatically updates nginx config
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com \
--email [email protected] \
--agree-tos \
--no-eff-email
The --no-eff-email flag prevents you from being signed up for promotional emails from EFF.
2. Verify Auto-Renewal
Certbot snap automatically installs a systemd timer for auto-renewal. Check it immediately after installation:
# Check if timer is active
sudo systemctl status snap.certbot.renew.timer
# Test the renewal process (without actually renewing)
sudo certbot renew --dry-run
--dry-run simulates the entire renewal process without modifying the real cert. If it fails here, it will fail for real during auto-renewal — better to catch it early now.
3. Verify Installed Certificates
# View all certificates and expiry dates
sudo certbot certificates
# Sample output:
# Found the following certs:
# Certificate Name: yourdomain.com
# Domains: yourdomain.com www.yourdomain.com
# Expiry Date: 2026-05-28 (VALID: 89 days)
# Certificate Path: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
4. Set Up Deploy Hook to Reload Nginx After Renewal
After certbot renews, you need to reload the web server to apply the new certificate. Certbot snap usually handles this automatically — but it’s not always reliable. It’s best to create an explicit deploy hook to be safe:
# Check existing deploy hooks
ls /etc/letsencrypt/renewal-hooks/deploy/
# Create hook to auto-reload nginx after successful renewal
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'EOF'
#!/bin/bash
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
From now on, every time certbot successfully renews, nginx reloads automatically. No more scenarios where the new cert is installed but nginx is still serving the expired old one.
Handling Common Errors
Error: “Problem binding to port 80”
# Nginx is blocking port 80 — stop temporarily, get cert, then restart
sudo systemctl stop nginx
sudo certbot certonly --standalone -d yourdomain.com
sudo systemctl start nginx
Error: “Connection timed out”
# Port 80 blocked by UFW
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
If using cloud, UFW is just one firewall layer — you also need to open the ports in Security Groups (AWS) or Firewall rules (GCP/DigitalOcean).
Emergency Renewal When Certificate Has Already Expired
# Force renew immediately
sudo certbot renew --force-renewal
# Reload nginx after renewal
sudo nginx -t && sudo systemctl reload nginx
Check SSL Grade After Installation
# Quick check from terminal
curl -vI https://yourdomain.com 2>&1 | grep -E "SSL|TLS|expire|issuer"
For a more thorough check, use SSL Labs at ssllabs.com/ssltest. It grades from A+ to F and pinpoints exactly where your config is weak — outdated cipher suites, HSTS not enabled, etc. Sites configured correctly with default certbot settings typically score an A or A+.
Finally, don’t ignore emails from Let’s Encrypt. They send alerts at the 30-day, 20-day, and 10-day marks before expiry — as long as you registered an email when running certbot. Receiving a warning email but auto-renew still not working means something needs to be debugged immediately. Don’t wait until the actual expiry date. That might be 2am.

