Install Certbot using Python PIP (Package Installer for Python) - without using SNAP, APT or SYSTEMD. (Debian/Ubuntu)
This guide will help you install LetsEncrypt / Certbot using venv PIP under Debian/Ubuntu.
-
This guide has been tested up to Debian 13 / Ubuntu 24 using the newest Certbot pip release.
-
You should already be somewhat familiar with LetsEncrypt, Certbot and any plugin you might need.
-
A DNS plugin (AWS Route53) is used here, but this guide is about the install method- not plugins, or validation methods.
-
This pip Python install method should also work on other Linux distributions that support python3, venv, and pip.
In my opinion, this is the best install method (as of now) as the APT version is always behind and I refuse to use SNAPD.
Brady Shea / bmatthewshea 28SEP2021
Script original location: https://gist.github.com/bmatthewshea/f6a66ddb2e52ccdbc905aed73d9ca59c
Last Updated: 26MAY2026:
- Updated the renewal hooks area to use
certbot reconfigureinstead of manually editing renewal files. - Minor typo fixes/etc.
Updated: 23APR2026:
- Added bit about how to remove systemd certbot service/timer if using crond.
Updated: 14APR2026:
- Added some more detail to cron job output (and to its generated email) to show server name and IP.
Updated: 1JAN2025:
- Noticed Debian 12+ minimal install does not include crontab package.
- Kept crontab method, but added SYSTEMD renewal method using a timer as well.
Updated: 12DEC2024:
- Updated "Cronjob" area of guide. Wrote about it's importance in renewals.
- Added a check to see if "cron" is installed/found while also checking cron.d file syntax.
- Apprently, Debian 12/Bookworm has no "cron" package installed by default (at least on a headless server).
Update: 24FEB2024:
- Updated 'upgrading' to include all components involved.
- Outline cleanup and correct URLs/anchors
- Other general cleanup/typos/etc
Update 31DEC2023:
- Updated to use approved/eff.org pip install instructions. (See references below)
- These steps now use
python3 -m venvinstead of system/repo/apt pip. - Change is due to
pyopensslcompatibility problems (the apt/repo version). (See comments) - Tested/working on Ubuntu 20, 22 and Debian 10, 11, 12.
- CERTBOT - Install using Python PIP
- 1. Cleanup, Install Python PIP, PIP dependencies and Certbot
- 2a. (Optional) Install needed plugins
- 2b. (Optional) Create an AWS-IAM credentials file
- 3. Dry Run and Execution
- 4. Setup automatic renewal (Optional, but recommended)
- 5. Setup renewal "hooks" (Optional, but recommended)
- 5. Expanding the certificate
- 6. Upgrading
- References
You should only run a single installed version of certbot.
You should not mix installation methods!
Optionally, some cleanup should be done just in case it's already installed/orphaned.
WARNING: If you have any certificates, you will need to recreate them after this!
Ignore any 'not found' errors:
sudo apt remove certbot* --purge # Purge any old certbots via apt.
sudo apt-add-repository --remove ppa:certbot/certbot # Remove certbot repo.
sudo snap remove certbot
sudo -H pip uninstall certbot; sudo -H pip3 uninstall certbot
pip uninstall certbot; pip3 uninstall certbot
sudo rm /usr/bin/certbot
sudo rm /usr/local/bin/certbot
rm ~/.local/usr/bin/certbot
rm ~/.local/bin/certbot
# You do not always need to delete the cert area, but it's usually best to start fresh:
sudo rm -rf /etc/letsencrypt
# Deactivate and remove any Certbot virtual Python environments you had running/setup.
# Example:
# `deactivate; sudo rm -rf /opt/certbot`
sudo apt update && sudo apt autoremove # Re-update and remove any orphaned packages.
Install Python 3 Pip under a virtual environment (we use /opt/certbot here) and upgrade it:
sudo apt update && sudo apt install python3 python3-venv libaugeas0
sudo python3 -m venv /opt/certbot/
sudo /opt/certbot/bin/pip install --upgrade pip
Install Certbot using venv Python pip to the virtual area and then symlink it to our path:
sudo /opt/certbot/bin/pip install certbot certbot
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
Before going any further, it's a good idea to make sure it is now installed and found in path:
certbot --version
Should give something like:
certbot x.x.x
Then try:
sudo certbot certificates
-
At this point you shouldn't have any unless you chose to keep the
/etc/letsencryptarea. -
The first run of "sudo certbot certificates" should also regenerate the
/etc/letsencryptarea if you wiped it.
(Optional - See #4 - Renewal) Install cron/crontab scheduler (you may skip this and just use a 'systemd' timer):
sudo apt install cron
Optional - If you use AWS-Route53 DNS you'll need this. Or, pick your own plugin (if needed):
sudo /opt/certbot/bin/pip install certbot-dns-route53
For errors such as:
ERROR: zope-component 5.0.1 has requirement zope.interface>=5.3.0a1, but you'll have zope-interface 4.7.1 which is incompatible.
Upgrade the individual python package mentioned in error:
sudo /opt/certbot/bin/pip install zope.interface --upgrade
You should see: "Successfully installed zope.interface-5.4.0" depending on name-version.
If errors were encountered during plugin installation, upgrading (or reinstalling) certbot (sudo /opt/certbot/bin/pip install --upgrade certbot) should also give 0 errors now. Re-check it is found and functions again per above (Step 1).
If so, continue on..
Again, I include this here for completeness only.
This guide is really about installing certbot using pip. If you do not use Route53, skip this.
Reference: https://certbot-dns-route53.readthedocs.io/en/stable/
sudo mkdir /root/.aws && sudo chmod 700 /root/.aws
sudo touch /root/.aws/credentials && sudo chmod 600 /root/.aws/credentials
sudo nano /root/.aws/credentials # (Add your own IAM creds in this file and save.)
Always do a "dry run" first - especially on a new install:
sudo certbot certonly --dry-run --agree-tos --dns-route53 --cert-name example -d example.net -d *.example.net
You should see: "The dry run was successful.". If so, execute same line w/o dry-run:
sudo certbot certonly --agree-tos --dns-route53 --cert-name example -d example.net -d *.example.net
If command completes successfully, you now have a functioning and up-to-date certbot installed via pip.
The importance of the "cron job" or systemd service/timer that schedules renewals is underated.
If it fails for any reason, your certificate may expire with little alerting.
PLEASE NOTE: Debian 12 minimal/headless install doen't include the cron* packages anymore. You can install it, or use 'systemd'.
I have included both the crontab and systemd methods of automatic renewal (USE ONLY ONE METHOD).
If you have an older system that had a repo or another version of certbot running, it is worth checking that you are running only one renewal .
If you wish to run renewal via crond, please make sure you're not still running the systemd timer & service:
Example:
sudo systemctl disable --now certbot.timer certbot.service
sudo rm /etc/systemd/system/certbot.service # Example path only
sudo rm /etc/systemd/system/certbot.timer # Example path only
Execute this command for a renewal cronjob. Update venv/Python path (/opt/certbot/bin) if it differs from guide:
echo "0 2 * * 0,3 root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo /usr/bin/certbot renew -q" | sudo tee /etc/cron.d/certbot > /dev/null
Please note that the official guide has this command tee'ed/appended to end of /etc/crontab.
I like to use /etc/cron.d/certbot instead.
I also recreate a new file (no tee flags as opposed to using -a append flag).
The cron job will execute at "0 2 * * 0,3" which is 2am every Sunday and Wednesday. Official guide has it execute 2x daily.
If getting emailed cron output, you could do something like this instead (shows initiating server's info):
SHELL=/bin/sh
# Grab public IP
public_ip=$(/usr/bin/curl -s4 ifconfig.me || /usr/bin/curl -s4 api.ipify.org)
# Show what server it is in output/email:
server_data="\nLetsEncrypt certificate renewal performed on: $(/usr/bin/hostname --fqdn) / ${public_ip}\n"
# Execute every Weds amd Sunday @2am with a delay of up to 1 hour/3600 seconds:
0 2 * * 0,3 root /usr/bin/test -x /usr/bin/certbot && /usr/bin/perl -e 'sleep int(rand(3600))' && /bin/echo -e "${server_data}" && /usr/bin/certbot renew
(See attached GIST for other alternatives)
Then run a check on your file -
crontab -n /etc/cron.d/certbot
That command will check your syntax and if command exists/installed.
It should come back with: The syntax of the crontab file was successfully checked.
It is worth checking that this file is being executed, or your renewal may FAIL.
If you prefer to use systemd (and *not* CRON above), be sure you are not already running a job under cron. Check the root crontab and cron.d locations - some example locations:
cat /etc/crontab # check this file for a certbot entry
sudo crontab -l # list root crontab (if you see an entry, '-e' edits)
sudo mv /etc/cron.d/certbot /etc/cron.d/certbot.disabled # or comment it out or delete it
Next, create the following two files:
sudo nano /etc/systemd/system/certbot.service
Add the following to the service file and save:
[Unit]
Description=Certbot Renewal
Documentation=https://certbot.eff.org/docs
[Service]
Type=oneshot
ExecStart=/opt/certbot/bin/certbot -q renew --no-random-sleep-on-renew
PrivateTmp=true
sudo nano /etc/systemd/system/certbot.timer
Add the following to the timer file and save:
[Unit]
Description=Run certbot renewal service twice daily
[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true
[Install]
WantedBy=timers.target
Run the following commands to enable timer/renew service:
sudo systemctl enable certbot.timer && sudo systemctl start certbot.timer
"Global" hook methods (operates on all renewals regardless of certificate name, or type):
- You can opt to add it to
cli.ini- Example:
post-hook /full/path/to/script/or/command
- Example:
- Or, directly on command line
- Example:
certbot renew --pre-hook /script/or/command --post-hook /script/or/command ...
- Example:
- Or, you can add a shell script to
/etc/letsencrypt/renewal-hooksin the/postor/preareas.
If you have many LE certificates and all of them need the same EXACT renewal parameters, you will probably want to use ONE of the "global" methods above such as the cli.ini file. You will add hook statements in same manner as the examples below, with the exception of the /renewal-hooks/ area. If you use this area, you create a shell script that stops, or restarts services (which amount to the same thing as the hook statements below).
For more granularity we are doing it a bit differently here:
We will run the certbot reconfigure command against a single renewal file to permanently modify it.
-
If you need to execute something before renew runs, you would add a
pre_hook =. An example of when this is needed is when usingstandaloneas the Certbot verification type. You will need to stop the system webserver before the renewal. You would issue a 'stop' on the pre_hook and a 'start' on the post_hook to Apache or Nginx (etc), so they aren't blocking the web port Certbot uses for verfication. -
If you need to execute something after renewal runs, you would add a
post_hook =. Once a renewal happens, reload or restart any daemons/services which depend on this renewed certificate. (Examples would be a webserver, email services, etc.)
Example #1 (DNS authentication used. We only need to restart services to pickup new certificate.):
sudo certbot reconfigure --cert-name example --post-hook "systemctl restart apache2"
Example #2 (DNS authentication used. We only need to restart services to pickup new certificate.):
sudo certbot reconfigure --cert-name example --post-hook "systemctl restart nginx postfix dovecot"
Example #3 (Web authentication used. Needs web port open. Stop/(RE)Start web server.):
sudo certbot reconfigure --cert-name example --pre-hook "systemctl stop nginx" --post-hook "systemctl restart nginx postfix"
Example #4 (Uses DNS authentication. Web server stop unneeded. Email only server with multiple ssl virual domains.):
sudo certbot reconfigure --cert-name example --post-hook "/usr/sbin/postmap -F hash:/etc/postfix/vmail_ssl.map; systemctl restart postfix dovecot"
Manually editing renewal files is not recommended. But if you wish to modify it, just add a pre-hook and/or post-hook under the "[renewalparams]" area of file. NOTE: This file may get overwritten in future and wipe out any manual changes you make to it!
Please also note that you could also call a shell script instead of actual directives/commands.
If you need to add (or delete!) domain(s) at a later date, you can use the --expand parameter to update it. Make sure you include ALL the original domains as well, or they will get removed (you will be prompted to continue before anything drastic happens).
sudo certbot certonly --expand --dns-route53 --cert-name example \
-d example.net -d *.example.net -d example.com -d *.example.com -d example.org -d *.example.org
To upgrade everything (and all dependencies), run these. You should probably do them seperately (do PIP first) and if you want to really stay up-to-date you should probably do this once a month - cronjob?:
sudo /opt/certbot/bin/pip install --upgrade pip
sudo /opt/certbot/bin/pip install --upgrade certbot
# And any plugin used:
sudo /opt/certbot/bin/pip install --upgrade certbot-dns-route53
https://letsencrypt.org/docs/
https://pypi.org/project/certbot/
https://certbot.eff.org/instructions?ws=other&os=pip
https://eff-certbot.readthedocs.io/en/stable
https://eff-certbot.readthedocs.io/en/stable/packaging.html
https://eff-certbot.readthedocs.io/en/stable/using.html#renewing-certificates
https://eff-certbot.readthedocs.io/en/stable/using.html#pre-and-post-validation-hooks
Entire guide has been updated to use https://certbot.eff.org/instructions?ws=other&os=pip method.
There is a conflict with the
pyopensslpackage as installed by Deb/Ubuntu repo/apt. So, I am avoiding the "system python/pip" / "repo version", now.(Certbot pip depends on
cryptographyand cryptography depends onpyOpenSSL.)This problem was brought to my attention by user "tanius" at the "AskUbuntu" stack site (Thanks, Tanius):
https://askubuntu.com/questions/1278936/install-certbot-on-ubuntu-20-04/1366594?noredirect=1#comment2618268_1366594