Let’s say you’re not a DevOps developer. You’re a pretty good front-end developer, a back-end developer, maybe even a mobile developer. But here’s the problem: you have a server, and it needs SSL. And the DevOps developer is on vacation. Or not hired at all. So you’re standing there, looking at the terminal and thinking: “Well, that’s it, now I’m a DevOps developer.” 😅
Don’t be scared. Today I’ll tell you how to quickly and automatically set up SSL certificates from Let’s Encrypt using Certbot on Ubuntu. With jokes, examples and bash magic.
🔒 Why do you need Certbot and Let’s Encrypt?
Let’s Encrypt is a free certificate authority. It allows you to issue SSL certificates for HTTPS. Certbot is a utility for automatically obtaining and renewing these certificates.
A combo for those who don’t want to bother with manual key generation and visits to commercial CAs.
🚀 Installing Certbot
sudo apt update
sudo apt install certbot
Important: Certbot must be run on the same server for which you want to issue a certificate.
📜 Obtaining a certificate (and displaying information about current certificates)
sudo certbot certonly --standalone -d project.com -d www.project.com
sudo certbot certificates
--standalone
means that Certbot will temporarily set up its own server on port 80 to validate the domain.- Make sure port 80 is not busy! Otherwise nothing will work.
If your Nginx or Docker is there, stop it while the command is running:
sudo systemctl stop nginx
Alternative: you can use a plugin webroot
– it does not require stopping the service and simply creates a special file in the site directory.
More information about plugins can be found in the official documentation .
⏱ Automatic update via systemd
After installation, Certbot automatically adds a systemd timer:
systemctl list-timers | grep certbot
Example output:
Mon 2025-03-10 03:12:00 UTC 10h left Mon 2025-03-09 03:12:00 UTC certbot.timer certbot.service
Check that auto-update works:
sudo certbot renew --dry-run
If everything is good:
Certbot dry-run was successful.
Latest update logs:
journalctl -u certbot.service --no-pager --since "2 days ago"
Reminder: Certbot only renews certificates if they have < 30 days left until expiration.
🕰️ Alternative: Update via Cron
Sometimes systemd timers are inconvenient, especially if you configure everything via Ansible or shell scripts. In this case, you can use Cron.
- Turn off the timer:
sudo systemctl stop certbot.timer
sudo systemctl disable certbot.timer
- Add a Cron task:
sudo crontab -e
Example:
0 0 1 * * /usr/bin/certbot renew --standalone --quiet
To find out the exact path to certbot
, use:
which certbot
⚙️ Hooks for restarting services
Certbot can perform actions before, after, or only if a certificate is successfully renewed. This is useful if you need to stop a service, copy certificates, or restart a server.
# /etc/letsencrypt/renewal/project.com.conf
pre_hook = systemctl stop my-project.service
deploy_hook = systemctl reload my-project.service
post_hook = systemctl start my-project.service
🛠️ Script for all occasions
Instead of describing everything in the cron, you can create a bash script that will:
- Stop service
- Update certificates
- Copy files to the desired location
- Set rights
- Start the service back
#!/bin/bash
APP_PATH="/home/projects/project"
DOMAIN="project.com"
FULL_CHAIN_SRC="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"
PRIVATE_KEY_SRC="/etc/letsencrypt/live/${DOMAIN}/privkey.pem"
FULL_CHAIN_DIST="${APP_PATH}/volumes/etc/ssl/certs/project_com.full.crt"
PRIVATE_KEY_DIST="${APP_PATH}/volumes/etc/ssl/private/project_com.key"
echo "Starting script execution: $(date "+%Y-%m-%d %H:%M:%S")"
echo "Stopping the service..."
systemctl stop my-project.service
echo "Renewing the certificates..."
/usr/bin/certbot renew --standalone --quiet
/usr/bin/certbot certificates
echo "Copying the certificates to the project..."
cp -f "$FULL_CHAIN_SRC" "$FULL_CHAIN_DIST"
cp -f "$PRIVATE_KEY_SRC" "$PRIVATE_KEY_DIST"
echo "Setting correct permissions for the certificates..."
chmod 644 "$FULL_CHAIN_DIST"
chmod 644 "$PRIVATE_KEY_DIST"
echo "Running the service..."
systemctl start my-project.service
echo "End of script execution: $(date "+%Y-%m-%d %H:%M:%S")"
And in the crown we call it like this:
0 0 1 * * sh /home/projects/project/cert-renew.sh >> /var/log/project-cert-renew.log 2>&1
🧠 Conclusion
Sometimes even a developer has to be a little bit of a DevOps. The main thing is not to be afraid of the terminal and know where to google. And also, don’t forget to check in six months that the auto-update still works. 😁