Setting up Let's Encrypt on Debian, AlmaLinux, and Ubuntu — three ways
Let's Encrypt is a free, automated, browser-trusted certificate authority. Three things are needed: a domain pointing at your VPS, a way to prove you control that domain (the ACME challenge), and a tool to do the negotiation. This article covers the three most common setups: nginx + certbot, Apache + certbot, and standalone certbot (for non-web services like mail).
Prerequisites — same for all three
- A domain name's A record pointing at your VPS's IP. Add it at your DNS provider and wait a minute or two for propagation.
- Port 80 open in your firewall. Even if you only want to serve HTTPS on port 443, the HTTP-01 challenge happens over port 80.
- A real email address for renewal-failure alerts.
Verify the domain resolves correctly before going further:
dig +short yourdomain.com
# should return your VPS IP
Option 1: nginx + certbot (webroot plugin)
The webroot plugin drops challenge files into your nginx document root and lets nginx serve them. Works without any nginx reconfiguration.
Debian / Ubuntu:
apt update && apt install -y certbot python3-certbot-nginx
AlmaLinux:
dnf install -y certbot python3-certbot-nginx
Run certbot in nginx mode — it'll edit your nginx config in place to add the cert and a port 443 server block:
certbot --nginx -d yourdomain.com -d www.yourdomain.com
Answer the prompts (email, agree to ToS, optionally redirect HTTP to HTTPS). Certbot writes the cert to /etc/letsencrypt/live/yourdomain.com/ and updates your nginx config to use it. Reload nginx if it doesn't do it automatically:
nginx -t && systemctl reload nginx
Renewal is automatic via a systemd timer (or cron job on older Debian). Verify:
systemctl list-timers | grep certbot
# Or test a dry-run renewal:
certbot renew --dry-run
Option 2: Apache + certbot
Same pattern, different plugin. Certbot's Apache plugin edits your vhost config in place.
Debian / Ubuntu:
apt update && apt install -y certbot python3-certbot-apache
certbot --apache -d yourdomain.com
AlmaLinux:
dnf install -y certbot python3-certbot-apache
certbot --apache -d yourdomain.com
Certbot creates or updates an SSL vhost in sites-available/ (Debian) or conf.d/ (RHEL family), and either reloads Apache or asks you to. Same renewal-timer pattern as nginx.
Option 3: standalone certbot (no web server, or non-web services)
If you don't run a web server (mail server, SIP server with HTTPS admin GUI, anything else needing a cert), certbot can briefly stand up its own listener on port 80 to do the challenge. Port 80 must be free during the renewal.
Install certbot only — no plugin needed:
# Debian / Ubuntu:
apt install -y certbot
# AlmaLinux:
dnf install -y certbot
Request the cert:
certbot certonly --standalone -d mail.yourdomain.com
The cert lands in /etc/letsencrypt/live/mail.yourdomain.com/ — point your service at fullchain.pem and privkey.pem there.
For automatic renewal, certbot will try to stand up its listener again. If your web server is running on port 80 by then, renewal fails. Two fixes:
- Renewal hooks: stop the conflicting service before renewal, start it after. Edit
/etc/letsencrypt/renewal/mail.yourdomain.com.confand add:pre_hook = systemctl stop nginx post_hook = systemctl start nginx - Webroot plugin instead: if you can serve the challenge from a web server you already run, drop standalone and use
--webroot -w /var/www/htmlagainst the existing nginx/Apache root.
Using the cert in a non-web service
Most services want the cert and key as separate files. Reference the symlinks under /etc/letsencrypt/live/yourdomain.com/:
fullchain.pem— your cert + intermediate chainprivkey.pem— the private keycert.pem— just your cert (no chain)chain.pem— just the intermediate
Examples — Postfix:
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.yourdomain.com/privkey.pem
Dovecot:
ssl_cert = </etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.yourdomain.com/privkey.pem
FreePBX®, FusionPBX®, asterisk's SIP-TLS — same pattern; the file paths above are stable across renewals (symlinks).
Restart-on-renew hooks
Most services need to be reloaded after a cert renews to pick up the new files. Use a deploy hook:
certbot renew --deploy-hook "systemctl reload postfix dovecot"
Or for services that need a full restart (some SIP daemons):
certbot renew --deploy-hook "systemctl restart asterisk"
You only need to pass the hook once — it's stored in the renewal config for that cert.
Troubleshooting
- "Failed authorization procedure" — almost always one of: A record doesn't resolve, port 80 is firewalled, or there's another HTTP server on port 80 conflicting with the challenge. Verify with
curl -v http://yourdomain.com/.well-known/acme-challenge/testfrom an outside machine. - Rate limit hit (50 certs / week per registered domain) — use
--dry-runwhile testing; switch to the Let's Encrypt staging environment with--stagingfor unlimited test runs. - Wrong cert served after renewal — the service is using a copied cert, not the symlink. Either point it at
/etc/letsencrypt/live/.../fullchain.pem(the symlink) or add a deploy hook to copy + reload.
If renewal silently fails, you'll get an email from Let's Encrypt about 20 days before expiration. Don't ignore those — they're your canary.
Also Read
Powered by WHMCompleteSolution