Cheat Sheet

Certbot Commands

Certbot is the standard client for getting free Let's Encrypt TLS certificates and renewing them on a schedule. This sheet covers the certbot commands for installing it, nginx and Apache, certonly with the standalone, webroot, and DNS challenges, wildcards, renewal, and deploy hooks.

Last updated September 11, 2026

Install

Snap is what Certbot's own docs recommend, because it always carries the current release. Distro packages lag behind but update with the rest of the system. The plugin for your web server is a separate package either way.

CommandWhat it does
Install the current Certbot on any distro with snap
Put the snap on ; skip this and you get "certbot: command not found"
Debian and Ubuntu, with the nginx plugin
Debian and Ubuntu, with the Apache plugin
Fedora, RHEL, Rocky, Alma
macOS, for local or DNS-only use
Confirm what you actually have

Without snap, a virtualenv keeps Certbot off the system Python:

sudo python3 -m venv /opt/certbot
sudo /opt/certbot/bin/pip install certbot certbot-nginx
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot

A pip or virtualenv install schedules nothing, so add the renewal cron entry yourself. Snap and distro packages both bring their own timer.

nginx

The nginx plugin is the usual route: it proves control of the domain, fetches the certificate, and edits the matching block in one go. It needs an existing block whose matches, which the nginx config generator can produce.

CommandWhat it does
Get and install a certificate, picking domains from your nginx config
Get and install for the listed domains
Get the certificate only; leave nginx config untouched
Wire an already-issued certificate into nginx
/ Force or skip the HTTP to HTTPS redirect (redirect is the default since Certbot 2.0)
Check and reload nginx after editing by hand

Confirm the result from outside with the SSL checker.

Apache

Same idea with the Apache plugin. On Debian and Ubuntu the service is ; on RHEL and friends it is .

CommandWhat it does
Get and install, picking domains from your VirtualHosts
Get and install for the listed domains
Get the certificate only; leave Apache config untouched
Check the config before reloading
Load the new certificate

certonly

obtains or renews a certificate without touching any web server config; you point the server at the files yourself. Pick one authenticator per run.

CommandWhat it does
Prove control through the running nginx
Prove control through the running Apache
Certbot runs its own temporary server on port 80
Drop challenge files into an existing site
Name the certificate instead of using the first domain

The files land in as and . Inspect what you got with the certificate decoder or from the OpenSSL cheat sheet.

Standalone

Standalone binds its own listener on port 80, so it works on machines with no web server at all: mail servers, database hosts, brokers. Whatever already holds port 80 must stop first.

CommandWhat it does
Get a certificate with certbot's built-in server
See what is currently holding port 80
Listen locally on another port behind a proxy; Let's Encrypt still connects to 80

If a web server owns port 80, stop it around the run and restart it after:

sudo certbot certonly --standalone -d mail.example.com \
  --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx"

Certbot remembers the hooks and reuses them at renewal, so the stop-start happens automatically from then on.

Webroot

Webroot never takes the site down: certbot writes the challenge into under the directory you name, and Let's Encrypt fetches it over plain HTTP.

CommandWhat it does
Get a certificate using the live site's document root
A second applies to the flags after it

One certificate for several sites, each with its own root:

sudo certbot certonly --webroot \
  -w /var/www/html -d example.com -d www.example.com \
  -w /var/www/blog -d blog.example.com

The path must be served over HTTP without a redirect to somewhere the challenge is not. A on during the run means points at the wrong directory.

DNS Challenge

The dns-01 challenge proves control with a TXT record, so it needs no open port at all, works behind a CDN, and is the only challenge that can issue wildcards.

CommandWhat it does
Ask for the DNS challenge
Check the record is visible before continuing
Let snap-installed plugins run with certbot
Install a DNS plugin (one exists per major provider)

Manual, for a one-off certificate:

sudo certbot certonly --manual --preferred-challenges dns -d example.com

Certbot prints the TXT value, you add it at your DNS host, wait for it to propagate, and press Enter. A manual certificate cannot renew unattended unless you also supply .

Automated, with a provider plugin and an API credentials file (chmod 600):

sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d example.com

Slow DNS host? Add (each plugin has its own version of the flag).

Every plugin follows the same three-part pattern: a package, a flag, and a credentials file.

PluginFlag
(reads standard AWS credentials)
(BIND and any RFC 2136 server)

Registrars with no official plugin, GoDaddy, Namecheap and Porkbun among them, have community plugins on PyPI, or you can delegate with a CNAME to a provider that does have one.

Wildcard Certificates

Wildcards come only from the DNS challenge above.

sudo certbot certonly --manual --preferred-challenges dns \
  -d example.com -d "*.example.com"
  • Quote so the shell does not try to expand it.
  • Include the bare domain: does not cover .
  • A wildcard covers one level only: yes, no.
  • For automatic renewal, use a DNS plugin instead of .

Renewal

Packaged installs (snap, apt, dnf) add a systemd timer or cron job that runs twice a day. Each certificate renews once it is within 30 days of expiry, reusing the exact options it was created with, so you never repeat or .

CommandWhat it does
Renew everything that is close to expiry
Full rehearsal against the staging server, changes nothing
Renew one certificate
Renew now regardless of expiry; rate limits apply
Confirm the renewal timer is scheduled
See the stored renewal settings for one certificate

The timer is a normal systemd unit, so the systemctl cheat sheet applies if it is missing or dead.

Add and Remove Domains

There is no "add domain" subcommand. You reissue the certificate with the full list of names you want and pin so it replaces the existing one instead of creating a second certificate under a new directory.

CommandWhat it does
Reissue with an extra name, same file paths
Skip the "expand this certificate?" prompt, for scripts
Drop every other name by reissuing with a shorter list
Issue with whatever names validate, instead of failing on one bad domain
Remove a whole certificate and stop renewing it

Get the current name list before you edit it, since leaving a domain out of the reissue removes it:

sudo certbot certificates --cert-name example.com

The live directory keeps its original name, so stays correct in nginx or Apache no matter which domains you add.

List and Delete

CommandWhat it does
List every certificate with domains, expiry, and file paths
Details for one certificate
Remove a certificate and its renewal config
Pick what to delete from a list
Tell Let's Encrypt the certificate is no longer valid
Why you are revoking (with )

does not touch your web server config. Remove the lines that point at the deleted files first, or nginx refuses to start on the next reload.

Deploy Hooks

Nginx and Apache load certificate files at startup and never look again, so a renewed file on disk changes nothing until a reload. A deploy hook runs after each successful issue or renewal, which is exactly the moment for it.

CommandWhat it does
Run a command after each successful renewal of this certificate
Executable scripts here run when any certificate renews
/ Run around every renewal attempt, successful or not
Include deploy hooks in a dry run (skipped otherwise)
, Set inside a deploy hook: the live directory path and the domains

Add a hook to an existing certificate without reissuing it:

sudo certbot reconfigure --cert-name example.com \
  --deploy-hook "systemctl reload nginx"

Files and Logs

Everything Certbot owns lives under , and every run appends to one log file. That log carries the real ACME error text, which is usually far more specific than what the terminal printed.

PathWhat it holds
and , the two files a web server points at
Every version ever issued; the live files are symlinks in here
The stored flags a renewal replays
Defaults applied to every run, e.g. or
Account keys, the thing worth backing up
Full output of the most recent run, older runs rotate to
CommandWhat it does
Read why the last run failed
Jump straight to the failure lines
Output of the snap renewal timer
Output of the deb or rpm renewal timer
/ More detail on screen during a run

Back up as a whole, with symlinks preserved (), or the restored copy points nowhere. See the tar cheat sheet.

Windows

Certbot no longer ships a native Windows build; EFF stopped producing the Windows installer during the 2.x series, and the old beta downloads are unsupported. Three options remain.

OptionWhat it means
WSLInstall Certbot in an Ubuntu WSL distro and copy the files to where Windows needs them
win-acmeThe maintained native Windows ACME client, with an IIS installer built in
posh-acmeA PowerShell ACME client, good for scripted or DNS-only issuance

IIS wants a , not the pair, so convert with from the OpenSSL cheat sheet.

Account and Version

CommandWhat it does
Create a Let's Encrypt account (normally happens on first run)
Show the account this machine uses
Change the contact email for expiry warnings
Deactivate the account
Print the installed version
The full option reference
Update a snap install (apt and dnf installs update with the system)

Docker

The official image is , and its entrypoint is certbot itself, so everything after the image name is a normal certbot command. Mount or the certificates die with the container.

docker run -it --rm -p 80:80 \
  -v /etc/letsencrypt:/etc/letsencrypt \
  certbot/certbot certonly --standalone -d example.com

Renewal is the same image with ; there is no timer inside a container, so schedule it yourself from host cron:

docker run --rm -p 80:80 \
  -v /etc/letsencrypt:/etc/letsencrypt \
  certbot/certbot renew
ImageWhat it is
The base image
Base image plus the Cloudflare DNS plugin (one image per provider)

For webroot next to an nginx container, mount the same volume in both containers as the challenge directory. Docker basics are on the Docker cheat sheet.

Command Line Options

OptionWhat it does
A domain to include; repeat for more
Which certificate to create or act on
/ Never prompt; fail instead (scripts)
Accept the terms of service (needed with )
Contact email for the account
Use the staging server: untrusted test certs, generous rate limits
Staging test that also saves nothing to disk
Add new domains to an existing certificate
Pick the challenge type (, )
RSA instead of the default ECDSA (default since Certbot 2.0)
Larger RSA key, with
/ Errors only, for cron output
More detail ( for debugging)
/ / Alternate directories, e.g. to run without root

Gotchas

  • Let's Encrypt rate limits are real: five identical certificates per week. Test with or , and never put in cron.
  • After a renewal your server still serves the old certificate until it reloads. If the SSL checker shows an expired certificate but shows a fresh one, the missing piece is a deploy hook.
  • Everything in is a symlink. Point your config there and never copy the files out; copies stop updating.
  • The HTTP challenge needs port 80 reachable from the internet. A closed firewall gives "Timeout during connect"; open it with (UFW cheat sheet) and confirm from outside with the port checker. Behind Cloudflare or another CDN, use the DNS challenge instead.
  • Failed validations have their own limit, five per account, hostname and hour. Retrying a broken challenge in a loop earns "too many failed authorizations recently" and an hour of lockout, so read and fix the cause between attempts.
  • and are long dead. Install from your package manager or snap ().

Certbot FAQ

Does Certbot renew certificates automatically?

Yes, if Certbot came from snap, apt, or dnf. Those packages install a systemd timer (or a cron job on older systems) that runs certbot renew twice a day. Each run only acts on certificates within 30 days of expiry, which is why most runs print 'not yet due for renewal' and exit without doing anything. Confirm the schedule exists with systemctl list-timers | grep certbot. A pip or from-source install schedules nothing, so you have to add the cron entry yourself. Renewal reuses the exact options stored in /etc/letsencrypt/renewal/example.com.conf, so you never repeat --nginx or -w, but it does not reload your web server unless a deploy hook tells it to.

Does Certbot need port 80?

Only for the HTTP challenge, which is what --nginx, --apache, --standalone, and --webroot all use. Let's Encrypt connects to port 80 on your public IP to fetch the challenge file, so that port has to be reachable even on a site that serves nothing but HTTPS. A redirect from 80 to 443 is fine, the validation server follows it. --http-01-port changes only the local listener behind a proxy, not the port Let's Encrypt dials, so it is not a way around a closed firewall. When port 80 genuinely cannot be opened, behind a CDN, on a private network, or on a host where another service owns it, use the dns-01 challenge, which needs no inbound connection at all.

How do I add a domain to an existing Certbot certificate?

Re-run certbot with the complete domain list and pin the certificate name: sudo certbot --nginx --cert-name example.com -d example.com -d www.example.com -d shop.example.com. Certbot sees the name already exists, issues a replacement covering the new set, and keeps the same /etc/letsencrypt/live/example.com/ paths, so nothing in your config changes. Add --expand to skip the confirmation prompt in a script. Removing a domain works the same way: run it again with the shorter -d list and Certbot reissues without the dropped names. Each reissue counts against the limit of 50 certificates per registered domain per week, so change the list once rather than adding subdomains one at a time.

How do I run a command after Certbot renews a certificate?

Attach a deploy hook: sudo certbot certonly --webroot -w /var/www/html -d example.com --deploy-hook 'systemctl reload nginx'. Certbot saves the hook in the certificate's renewal config and runs it after every successful renewal of that certificate, which is exactly when nginx or Apache needs a reload to pick up the new file. To run something for every certificate on the machine, drop an executable script into /etc/letsencrypt/renewal-hooks/deploy/ instead. Inside a hook, $RENEWED_LINEAGE holds the live directory path. Note that renew --dry-run skips deploy hooks unless you add --run-deploy-hooks.

Where does Certbot store certificates?

Under /etc/letsencrypt/live/<cert-name>/, where fullchain.pem and privkey.pem are the two files a web server needs. They are symlinks into /etc/letsencrypt/archive/, and renewal repoints them at the newest version, so reference the live paths in your config and never copy the files somewhere else or they will silently go stale. Renewal settings live in /etc/letsencrypt/renewal/<cert-name>.conf, global defaults in /etc/letsencrypt/cli.ini, and the log of every run in /var/log/letsencrypt/letsencrypt.log. sudo certbot certificates prints every certificate with its domains, expiry date, and paths.

Related cheat sheets