In short: Let's Encrypt is a non-profit certificate authority that issues SSL certificates for free and fully automatically: the certbot client proves domain ownership over the ACME protocol, installs the certificate into nginx or Apache, and renews it without human involvement. Below: how to get a free SSL certificate in 10 minutes, how to issue a wildcard for all subdomains, how to renew certificates automatically, and which typical issuance errors waste the most time.

Since 2016 the Let's Encrypt certificate has become the de facto standard for small and mid-size businesses: setup takes 10 minutes, renewal happens by itself, and every modern browser trusts the result. This is a complete guide to installing certbot on Ubuntu/Debian and CentOS/AlmaLinux with nginx and Apache examples — plus a comparison of alternative ACME clients, wildcard issuance via DNS-01, and a final checklist.
What Let's Encrypt is and why the certificate is free
Let's Encrypt is a project of the non-profit Internet Security Research Group (ISRG), funded by Mozilla, EFF, Cisco, Chrome, and hundreds of other sponsors. The project's goal is a fully encrypted web, so an SSL certificate is issued free of charge to any domain owner: no paperwork, no sales calls, no hidden fees. Technically it is a DV (Domain Validation) certificate: it proves control of the domain, not the legal entity behind it — that is what OV and EV are for; the difference is covered in SSL certificate types. For actual traffic encryption, DV gives you exactly the same algorithms and the same protection as paid certificates.
Let's Encrypt rate limits (current list on letsencrypt.org):
- 50 certificates per week per registered domain.
- 100 SANs (names) per certificate.
- 5 duplicate certificates (the same set of names) per week.
- 300 new orders per 3 hours per account.
How ACME works: why issuance is automatic
ACME (Automatic Certificate Management Environment, RFC 8555) is a standardized protocol that lets a client on your server talk to the certificate authority without a human in the loop. The client creates an account with a key pair, requests a certificate, and proves domain ownership with one of three challenge types:
- HTTP-01 — the CA fetches a file from
http://yourdomain/.well-known/acme-challenge/<token>. Requires a public IP and an open port 80. The simplest option for a regular website; wildcards cannot be issued this way. - DNS-01 — the client publishes a TXT record at
_acme-challenge.yourdomain. No web server or public IP is needed at all; this is the only way to get a wildcard certificate. - TLS-ALPN-01 — validation inside the TLS handshake on port 443 using a dedicated ALPN extension. Used less often, mostly by proxies and load balancers where port 80 is closed.

The core idea of ACME: no human takes part in issuance at all. If getting or renewing a certificate requires you to log in somewhere and download an archive by hand, the process is built wrong and will eventually break.
Installing certbot
Ubuntu/Debian:
sudo apt update
sudo apt install certbot python3-certbot-nginx
# for Apache:
sudo apt install certbot python3-certbot-apache
CentOS/RHEL/AlmaLinux:
sudo dnf install certbot python3-certbot-nginx
Universal via snap (recommended on modern systems):
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Check: certbot --version should show 2.0+.
How to get a free SSL certificate for nginx
Easiest — the nginx plugin edits your config for you:
sudo certbot --nginx -d example.com -d www.example.com
Certbot asks for an email (renewal notifications), the ToS agreement, and issues the certificate. Files land in /etc/letsencrypt/live/example.com/. Your nginx config gets:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
Verify: sudo nginx -t && sudo systemctl reload nginx. Open https://example.com — the padlock should appear in the address bar. Apache works the same way: sudo certbot --apache -d example.com.
Manual mode via webroot
If you don't want certbot touching your nginx config (because you use a template or Ansible), fetch only the certificate:
sudo certbot certonly --webroot -w /var/www/html \
-d example.com -d www.example.com
nginx must serve /.well-known/acme-challenge/ from /var/www/html:
location /.well-known/acme-challenge/ {
root /var/www/html;
}
Then wire up the certificate paths in your SSL config and reload.
Standalone mode (no web server)
When no web server is configured yet, or for non-standard ports:
sudo systemctl stop nginx
sudo certbot certonly --standalone -d example.com
sudo systemctl start nginx
Certbot temporarily runs its own server on port 80. The port must be free — stop nginx/apache first.
Wildcard certificate for subdomains
A wildcard certificate like *.example.com covers every first-level subdomain: app, api, mail, staging — as many as you need, with no reissue for each new one. It can only be issued via the DNS-01 challenge: you must prove control over the entire DNS zone, not a single host, so HTTP-01 fundamentally cannot work for wildcards.

Cloudflare example:
# Install the plugin
sudo snap install certbot-dns-cloudflare
# Token file
sudo tee /etc/letsencrypt/cloudflare.ini <<EOF
dns_cloudflare_api_token = YOUR_TOKEN
EOF
sudo chmod 600 /etc/letsencrypt/cloudflare.ini
# Issue the wildcard
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d "*.example.com" -d example.com
Similar plugins exist for Route 53, DigitalOcean, and Google Cloud DNS. If your DNS provider has no API, there is a manual mode with --manual --preferred-challenges dns — but then renewal becomes manual too, which defeats the point of Let's Encrypt. When a wildcard makes sense and when listing subdomains as SANs is enough — see wildcard SSL certificates.
Alternative ACME clients
Certbot is the official client but not the only one. Sometimes another tool fits better:
| Client | What it is | When to choose it |
|---|---|---|
| certbot | The official EFF client in Python with nginx/Apache and DNS plugins | A classic VPS with nginx or Apache; the most documentation available |
| acme.sh | A pure-shell client with no dependencies and 150+ DNS providers | Minimal systems, containers, routers, shared hosting without root |
| Caddy | A web server with built-in ACME: certificates are issued on first request | New projects where you want HTTPS out of the box with no separate client |
| Traefik | A reverse proxy for Docker/Kubernetes with a built-in ACME resolver | Docker stacks and microservices: per-domain certificates without certbot |
How to renew an SSL certificate: why 90 days and how not to miss it
A Let's Encrypt certificate lives for 90 days. That is deliberate: a stolen key stays useful for three months at most, and the short lifetime forces automation — nobody will renew by hand every quarter. Certbot renews on its own: the installer creates a systemd timer (or a cron job) that runs certbot renew twice a day. The actual renewal only happens once fewer than 30 days remain.

sudo systemctl status certbot.timer
# or cron:
cat /etc/cron.d/certbot
Test the renewal pipeline in advance — this simulates the full cycle against staging and spends none of your rate limits:
sudo certbot renew --dry-run
To make nginx pick up the fresh certificate, add a deploy hook:
# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/bash
systemctl reload nginx
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
An expired certificate is the most common cause of a sudden "the site is down". The timer can die quietly after a migration, an OS upgrade, or a full disk — and nobody notices until browsers start throwing errors.
So do not rely on certbot alone: check your domain in the SSL checker, set a reminder a couple of weeks before expiry, and configure certificate expiry monitoring with alerts. If the certificate has already expired, there is a step-by-step recovery plan in fixing an expired SSL certificate.
Typical issuance errors
- "DNS problem: NXDOMAIN" — the domain does not resolve. Check the A/AAAA records and for typos in
-d. - "Failed authorization: Invalid response from http://..." — port 80 is blocked by a firewall or the webroot is misconfigured. Make sure
/.well-known/acme-challenge/is reachable from outside: HTTP-01 works over port 80 only. - "too many certificates already issued" — you hit the 50-per-week cap for the domain. Experiment against staging with
--test-cert. - "too many certificates already issued for exact set of domains" — the 5-duplicates-per-week limit: you keep issuing a certificate for the same set of names. Reuse the one you already have (
certbot certificates) instead of reissuing. - "CAA record prevents issuance" — the domain's CAA record forbids issuance. Check
dig example.com CAA: it must allowletsencrypt.orgor be absent. - DNS has not propagated yet — after changing an A record or adding the DNS-01 TXT record, wait out the TTL; certbot can wait via
--dns-<plugin>-propagation-seconds. - "unable to get local issuer certificate" — the server sends an incomplete chain. Diagnostics in cannot verify server certificate.
- Android < 7.1.1 does not trust ISRG Root X1 — old devices need the long (cross-signed) chain; enable the short chain only if all your clients are modern.
Frequently asked questions
Is Let's Encrypt appropriate for commercial sites?
Yes. Let's Encrypt is used by Cloudflare, WordPress.com, Mozilla, and millions of commercial sites. The only limitation: no OV/EV certificates with legal-entity validation.
Why are certificates issued for 90 days instead of a year?
The short lifetime limits the damage from a compromised key and forces renewal automation. With a working certbot.timer you will never notice the difference between 90 days and a year.
How many domains can one certificate include?
Up to 100 SAN names. In practice one certificate per site (domain + www) is more convenient than a bag of dozens of names: any change to the set requires a reissue.
How do I issue a certificate behind NAT or without a public IP?
Use the DNS-01 challenge — it needs no inbound HTTP access. The server does not have to be reachable from the internet at all; this is how certificates for intranet services are issued.
Can I use Let's Encrypt on Windows Server?
Yes. For IIS there is the win-acme client with full ACME support; the principle is the same — automatic issuance and scheduled renewal.
How do I verify the certificate is installed correctly?
Open the site over https and test the domain with an external tool — see how to check an SSL certificate: it shows the chain, the expiry date, and typical configuration problems.
Checklist: free SSL from issuance to auto-renewal
- The domain resolves to the server (A/AAAA), port 80 is open for HTTP-01.
- Certbot is installed (snap or package),
certbot --versionshows 2.0+. - The certificate is issued:
certbot --nginxorcertonly --webroot; for subdomains — a wildcard via DNS-01. nginx -tpasses, the site opens over https with no warnings.sudo certbot renew --dry-runfinishes without errors.systemctl status certbot.timer— the timer is active.- A deploy hook reloads nginx after each renewal.
- Expiry monitoring is configured, the domain is verified in the SSL checker.
Conclusion
Let's Encrypt + certbot is the fastest way to a valid SSL certificate for free: 10 minutes to install, automatic renewal, and trust from every browser. The key is not to stop at issuance: run renew --dry-run, make sure the timer is alive, and add external expiry monitoring. Then you can safely forget about the certificate.
Certbot documentation — certbot.eff.org. Let's Encrypt documentation — letsencrypt.org/docs.