cPanel's AutoSSL handles certificates for shared hosting, but on an unmanaged VPS there's no cPanel to do that job for you. If you're running plain Nginx or Apache on Ubuntu or CentOS/AlmaLinux, you need to install and renew SSL certificates yourself — and the tool for that is Certbot, the official Let's Encrypt client. It's free, it's scriptable, and once it's set up correctly it renews itself without you touching the server again. Here's how to do it properly, including the mistakes that cause it to fail three months later.
Symptom
You'll typically end up here because of one of these:
- You just provisioned a fresh VPS and your site is only reachable over
http:// - Browsers show Not Secure or refuse to load the site at all with
ERR_CONNECTION_REFUSEDon port 443 - You installed a certificate once, manually, and it expired 90 days later because nothing renewed it
nginx -tor Apache's config test fails after you tried to reference a cert file that was never generated
Before You Start: What Certbot Actually Needs
Let's Encrypt issues certificates by proving you control the domain. On a VPS, that almost always happens over HTTP or via the web server plugin, which means three things have to be true first:
- Your domain's A record already points at this VPS's public IP (check with
dig +short yourdomain.com) - Port 80 is open in your firewall, at least temporarily — Let's Encrypt's HTTP-01 challenge needs it
- Nginx or Apache is already installed and serving something on that domain, even a placeholder page
If DNS hasn't propagated yet, stop here and wait. Certbot will fail every time against a domain that doesn't resolve to this server, and repeated failed attempts count toward Let's Encrypt's rate limits.
Step 1: Install Certbot
Skip your distro's default package repo for Certbot if it's old — use the official snap install, which Let's Encrypt itself recommends because it stays current:
sudo apt update
sudo apt install snapd -y
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
On CentOS/AlmaLinux/Rocky, snapd works the same way after enabling EPEL, or you can use dnf install certbot python3-certbot-nginx if you don't mind a slightly older version.
Step 2: Install the Plugin for Your Web Server
The Nginx and Apache plugins let Certbot edit your server config automatically — adding the cert paths and setting up the HTTP-to-HTTPS redirect for you.
# For Nginx
sudo snap install certbot --classic # if not already done
sudo apt install python3-certbot-nginx -y
# For Apache
sudo apt install python3-certbot-apache -y
Step 3: Request and Install the Certificate
Make sure you have a working server_name (Nginx) or ServerName (Apache) directive pointing at your domain in the site's config file first — Certbot reads that to know which block to modify. Then run:
# Nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Apache
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
Certbot will ask for an email (for renewal and expiry notices) and whether to redirect all HTTP traffic to HTTPS — say yes unless you have a specific reason not to. It fetches the certificate, writes it to /etc/letsencrypt/live/yourdomain.com/, and edits your Nginx or Apache config in place, keeping a backup of the original.
If you'd rather review the config change yourself before it touches anything, add --nginx (or --apache) with the certonly subcommand instead, and wire the paths into your config manually:
sudo certbot certonly --nginx -d yourdomain.com -d www.yourdomain.com
Step 4: Confirm the Renewal Timer Is Actually Running
This is the step people skip, and it's why certificates expire silently. The snap install sets up a systemd timer automatically — verify it exists:
systemctl list-timers | grep certbot
You should see a snap.certbot.renew.timer entry. If you installed Certbot through apt instead of snap, check for a cron job or systemd timer under /etc/cron.d/certbot instead. Either way, do a dry run to confirm renewal actually works before you trust it:
sudo certbot renew --dry-run
If that comes back clean with no errors, you're set for the next two years without thinking about this again. Certbot renews certificates automatically once they're within 30 days of expiry — it doesn't wait for day 90.
Common Failures and Fixes
| Error | Cause | Fix |
|---|---|---|
| Timeout during connect / "Fetching http://... Timeout" | Port 80 blocked by firewall (UFW, cloud security group, or both) | sudo ufw allow 80,443/tcp and check your VPS provider's firewall/security group rules too |
| "DNS problem: NXDOMAIN looking up A record" | Domain doesn't resolve to this server yet | Wait for DNS propagation, confirm with dig +short yourdomain.com |
| "Too many certificates already issued" | Hit Let's Encrypt's rate limit (5 duplicate certs per week per domain set) | Wait it out, or use the staging environment (--staging) while testing your setup |
| Certificate installed but browser still shows a warning | Cloudflare or another CDN is proxying traffic and presenting its own cert, or the redirect wasn't added | Set Cloudflare SSL mode to "Full (strict)", or add the redirect block manually |
| Renewal fails months later with a permissions error | Config files were manually edited and broke the ACME challenge path | Check /var/log/letsencrypt/letsencrypt.log for the specific line that failed, then re-run certbot --nginx to have it repair the block |
Prevention
- Always run
certbot renew --dry-runright after setup, not just when something breaks — it takes ten seconds and confirms the whole chain works - Don't hand-edit the
ssl_certificatelines Certbot inserted unless you know exactly what you're changing; let Certbot manage that block - If you're behind Cloudflare, set SSL mode to Full (strict) so Cloudflare also validates your origin certificate instead of just trusting whatever's there
- Keep port 80 open permanently, even after you force HTTPS redirects — the renewal process needs it for the HTTP-01 challenge every time
- Add a simple monitoring check (even a cron job that curls your site and emails you on a non-200 or cert-expiry warning) so a failed renewal doesn't sit unnoticed for weeks
Frequently Asked Questions
Do I need Certbot if I'm using cPanel?
No. cPanel handles this itself through AutoSSL, which uses Let's Encrypt (or Sectigo) behind the scenes without any command-line steps. Certbot is for unmanaged VPS setups running plain Nginx or Apache without a control panel.
Is Let's Encrypt good enough for a production site, or do I need a paid certificate?
Let's Encrypt certificates use the same encryption strength as paid ones and are trusted by every major browser. The only thing you don't get is extended validation (the old green-bar identity display, which browsers barely show anymore anyway) or a warranty from the CA. For the vast majority of sites, it's genuinely enough.
Why did my certificate expire even though I set up Certbot?
Almost always because the renewal timer or cron job wasn't actually running — check with systemctl list-timers | grep certbot. The second most common cause is a firewall change made after setup that quietly blocked port 80, which the renewal process needs even if your site force-redirects to HTTPS.
Can I get one certificate that covers multiple subdomains?
Yes. Add multiple -d flags in a single command (e.g. -d yourdomain.com -d www.yourdomain.com -d shop.yourdomain.com) and Certbot issues one multi-domain certificate covering all of them. For a wildcard certificate covering every subdomain automatically, you'll need the DNS-01 challenge instead, which requires a DNS plugin for your provider.
What happens if I switch web servers later, say from Apache to Nginx?
The certificate files themselves live independently in /etc/letsencrypt/live/ and aren't tied to a specific web server. You'll need to reference the correct paths in the new server's config and reinstall the matching Certbot plugin, but you won't need to re-issue the certificate from scratch.
