If you’ve ever lost an afternoon to SSL/TLS configuration, you’re in good company. That’s exactly why I put together this guide.
In the following sections, we’ll configure Let’s Encrypt certificates using Certbot. Since Let’s Encrypt provides certificates free of charge, there’s no reason to leave a website unencrypted. We’ll automate renewals and make sure your website can communicate securely without requiring constant supervision. Because some things deserve attention and certificate expiration dates are not one of them.
Step 1: Install Certbot
The installation process varies slightly depending on your operating system and CPU architecture. In this guide, we use the standalone method to issue the certificate directly, without relying on a web server already running on the machine.
Debian / Ubuntu
sudo apt update
sudo apt install certbot -y
Oracle Linux 7 / RHEL 7 (Standard x86_64)
On standard instances, you need to enable the EPEL repository before installing via yum:
sudo yum install oracle-epel-release-el7 -y
sudo yum install certbot -y
Oracle Linux 7 on ARM (aarch64 / OCI Ampere)
ARM-based instances, very common in Oracle Cloud’s Free Tier often do not have a pre-compiled Certbot package available in EPEL. The cleanest workaround is installing via pip, pinning specific dependency versions to avoid compilation errors on Python 3.6:
# 1. Install Python 3 and PIP
sudo yum install python3 python3-pip -y
1. 2. Upgrade PIP and Setuptools
sudo pip3 install --upgrade pip setuptools
1. 3. Install specific dependency versions and Certbot
sudo pip3 install cryptography==3.3.2 pyOpenSSL certbot
1. 4. Create a symlink to run it globally
sudo ln -s /usr/local/bin/certbot /usr/bin/certbot
Note: you may see a WARNING about running pip as root. In a dedicated lab or isolated server environment, this is generally safe to proceed with.
Step 2: Open Port 80 for Validation
Certbot uses port 80 (HTTP) to validate domain ownership. Make sure it is open both on the local firewall and in your cloud provider’s network rules.
Local Firewall
Debian (UFW):
sudo ufw allow 80/tcp
Oracle Linux 7 (Firewalld):
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --reload
Cloud Provider Network Configuration
Regardless of local firewall settings, inbound traffic on port 80 must also be permitted at the network level whether through OCI Security Lists, AWS Security Groups, or Azure Inbound Rules.
Step 3: Run Certbot to Get the Certificate
Before running the command, confirm that your domain points to the server’s public IP via an A Record at your DNS provider. Wait for propagation, then proceed:
sudo certbot certonly --standalone -d yourdomain.com
Important: no web server or Docker container can be occupying port 80 during this process. If you have Docker mapped to that port, stop the service temporarily:
sudo systemctl stop docker docker.socket
Step 4: Where Are My Certificates?
After a successful issuance, Certbot will display the paths to the generated files. Keep these close, you will need them:
- Full chain:
/etc/letsencrypt/live/yourdomain.com/fullchain.pem - Private key:
/etc/letsencrypt/live/yourdomain.com/privkey.pem
Step 5: Automating Renewal with Nginx Proxy Manager (Docker)
If you are running Nginx Proxy Manager (NPM) via Docker, it is possible to fully automate the renewal process straight from the terminal. When a certificate is added as a “Custom” provider in the NPM web interface, it gets copied into an internal container directory. The script below keeps everything in sync, no manual intervention required.
1. Create the automation script
sudo nano /usr/local/bin/renova_certbot_npm.sh
2. Paste the following content
Adjust the domain name, container name, and custom_ssl folder ID to match your environment:
#!/bin/bash
1. 1. Stop Docker to free up port 80
systemctl stop docker docker.socket
1. 2. Run Certbot silent renewal
/usr/bin/certbot renew --quiet
1. 3. Start Docker again
systemctl start docker
1. 4. Wait for the NPM container to fully boot
sleep 10
1. 5. Copy the real certificates into NPM (using -L to follow symlinks)
docker cp -L /etc/letsencrypt/live/yourdomain.com/fullchain.pem npm_container_name:/data/custom_ssl/npm-X/fullchain.pem
docker cp -L /etc/letsencrypt/live/yourdomain.com/privkey.pem npm_container_name:/data/custom_ssl/npm-X/privkey.pem
1. 6. Reload Nginx configuration
docker exec npm_container_name nginx -s reload
3. Make the script executable
sudo chmod +x /usr/local/bin/renova_certbot_npm.sh
4. Schedule it with Cron
sudo crontab -e
Add the following line to check twice a day:
0 0,12 * * * /bin/bash /usr/local/bin/renova_certbot_npm.sh
A note on the NPM web console: because the certificate was manually added as a “Custom” provider, the expiration date displayed in the interface will not update visually. This is expected behavior, what matters is that the actual certificate files on disk are continuously renewed and valid through the background script.
Final Thoughts
Properly implementing SSL/TLS is one of the most fundamental and most overlooked habits in system administration. An expired certificate does not warn you before it breaks production. It just breaks it. By automating renewals with a dedicated script and integrating directly with Docker via the command line, you eliminate human error, work around architectural limitations, and keep your proxies running without surprises.
No good sysadmin leaves the front door ajar. And no well-managed certificate expires without someone knowing about it.