This guide will help you use Certbot to get free SSL certificates for your website. Certbot is a simple, automatic tool that makes your website secure (the padlock icon in your browser) and keeps it that way with automatic renewals.
Before you start: Make sure you have already installed Certbot on your server. If you have not, you will need to complete the installation first.
Let's start by getting your first SSL certificate. This process will prove you own your domain and get you a trusted certificate.
First, you need to load your account information. This tells Certbot who you are:
source /root/.secrets/acme-eab.env
This command loads your certificate provider credentials from a secure location on your server.
Now, let's get your SSL certificate. Here is the command for a single domain:
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
--dns-cloudflare-propagation-seconds 30 \
--server "$ACME_SERVER" \
--eab-kid "$EAB_KEY" \
--eab-hmac-key "$HMAC_KEY" \
--email "$EMAIL" \
--agree-tos \
--no-eff-email \
--non-interactive \
--issuance-timeout 600 \
-d demo.example.com
Replace demo.example.com with your actual domain name.
What this command does:
If everything worked, you will see a success message like this:
Requesting a certificate for demo.example.com
Waiting for verification...
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/demo.example.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/demo.example.com/privkey.pem
Congratulations! You now have a valid SSL certificate.
Depending on your needs, you can get different types of certificates:
If you want one certificate to cover several related domains, add multiple -d flags:
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
--dns-cloudflare-propagation-seconds 30 \
--server "$ACME_SERVER" \
--eab-kid "$EAB_KEY" \
--eab-hmac-key "$HMAC_KEY" \
--email "$EMAIL" \
--agree-tos \
--no-eff-email \
--non-interactive \
--issuance-timeout 600 \
-d example.com \
-d www.example.com \
-d mail.example.com
Use this when: You have a few specific domains that belong together.
This covers ALL subdomains under your main domain automatically:
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
--dns-cloudflare-propagation-seconds 30 \
--server "$ACME_SERVER" \
--eab-kid "$EAB_KEY" \
--eab-hmac-key "$HMAC_KEY" \
--email "$EMAIL" \
--agree-tos \
--no-eff-email \
--non-interactive \
--issuance-timeout 600 \
-d "*.example.com" \
-d example.com
Use this when: You have many subdomains or create them frequently.
Example: One certificate covers blog.example.com, shop.example.com, api.example.com, and any other subdomain you create.
After getting a certificate, Certbot saves several files in a specific folder:
/etc/letsencrypt/live/demo.example.com/
??? cert.pem # Your certificate only
??? chain.pem # Intermediate certificates
??? fullchain.pem # Complete certificate (USE THIS!)
??? privkey.pem # Private key (KEEP SECRET!)
??? README # Information file
| File | Description |
|---|---|
fullchain.pem | THIS IS THE ONE YOU WILL USE! Contains your complete certificate with all necessary parts. Use this for Apache, Nginx, and most web servers. |
privkey.pem | VERY IMPORTANT — KEEP THIS SECRET! This is your private key. NEVER share this with anyone. Do not upload it to public places. Keep permissions restricted (only readable by root). |
cert.pem | Your certificate only (without intermediate certificates). |
chain.pem | Intermediate certificates only (without your domain certificate). |
???? Note These files are symbolic links (shortcuts) that point to the real certificates stored in /etc/letsencrypt/archive/. This system makes it easy to update certificates without changing your web server configuration. |
SSL certificates expire after 90 days. Certbot can automatically renew them before they expire. You have two options:
Modern Linux systems use systemd timers. Certbot usually sets this up automatically during installation.
sudo systemctl status certbot.timer
This shows if automatic renewal is running.
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
These commands turn on automatic renewal and start it running.
sudo systemctl list-timers certbot.timer
This shows when Certbot will next check for renewals. Expected output:
NEXT LEFT LAST PASSED UNIT
Sun 2026-02-10 00:00:00 UTC 13h left Sat 2026-02-09 00:00:00 UTC 11h ago certbot.timer
Sun 2026-02-10 12:00:00 UTC 1h left Sat 2026-02-09 12:00:00 UTC 23h ago certbot.timer
This shows Certbot runs twice daily to check for certificates that need renewal.
Before relying on automatic renewal, test it to make sure it works:
sudo certbot renew --dry-run
The --dry-run flag means "test mode" — it will not actually renew anything, just checks that renewal would work.
If your system does not use systemd, you can use a traditional cron job:
sudo crontab -e
This opens a text editor where you can add scheduled tasks.
Add this line to run renewal twice daily (at 2:30 AM and 2:30 PM):
30 2,14 * * * /usr/bin/certbot renew --quiet --deploy-hook "/usr/local/bin/deploy-cert.sh"
What this means:
30 2,14 * * * — Run at 2:30 AM and 2:30 PM every day--quiet — Do not show output unless there is an error--deploy-hook — Run a script after renewal to update your web serverWhen Certbot renews a certificate, you need to tell your web server to use the new certificate. A deployment script does this automatically.
This creates a script that will run automatically after each renewal:
sudo tee /usr/local/bin/deploy-cert.sh > /dev/null << 'EOF'
#!/bin/bash
# Automatically update web servers after certificate renewal
LOG_FILE="/var/log/cert-deploy.log"
log() {
echo "$(date '%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# Get the renewed domain name
PRIMARY_DOMAIN=$(echo "$RENEWED_DOMAINS" | awk '{print $1}')
log "Certificate renewed for: $PRIMARY_DOMAIN"
# Restart Apache (if installed)
if command -v apache2ctl &> /dev/null; then
systemctl reload apache2
log "Apache reloaded"
fi
# Restart Nginx (if installed)
if command -v nginx &> /dev/null; then
systemctl reload nginx
log "Nginx reloaded"
fi
log "Deployment completed"
EOF
This script does the following:
sudo chmod +x /usr/local/bin/deploy-cert.sh
This gives the script permission to run.
Before trusting automatic deployment, test the script manually:
export RENEWED_DOMAINS="demo.example.com"
export RENEWED_LINEAGE="/etc/letsencrypt/live/demo.example.com"
sudo /usr/local/bin/deploy-cert.sh
Check the log file to see if it worked:
sudo tail /var/log/cert-deploy.log
Now that you have a certificate, you need to configure your web server to use it.
sudo tee /etc/apache2/sites-available/demo.example.com-ssl.conf > /dev/null << 'EOF'
<VirtualHost *:443>
ServerName demo.example.com
ServerAdmin admin@demo.example.com
DocumentRoot /var/www/demo.example.com
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/demo.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/demo.example.com/privkey.pem
# Modern SSL configuration (Mozilla Intermediate)
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
SSLHonorCipherOrder off
SSLSessionTickets off
# HSTS (optional but recommended)
Header always set Strict-Transport-Security "max-age=63072000"
<Directory /var/www/demo.example.com>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/demo.example.com-error.log
CustomLog ${APACHE_LOG_DIR}/demo.example.com-access.log combined
</VirtualHost>
EOF
? Important Replace demo.example.com with your actual domain name throughout the configuration. |
sudo a2enmod ssl headers
sudo a2ensite demo.example.com-ssl
These commands enable SSL support and activate your secure site.
sudo apache2ctl configtest
sudo systemctl reload apache2
The first command checks for errors. The second applies the changes.
sudo tee /etc/nginx/sites-available/demo.example.com > /dev/null << 'EOF'
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name demo.example.com;
root /var/www/demo.example.com;
index index.html index.php;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/demo.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/demo.example.com/privkey.pem;
# Modern SSL configuration (Mozilla Intermediate)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# HSTS (optional but recommended)
add_header Strict-Transport-Security "max-age=63072000" always;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
location / {
try_files $uri $uri/ =404;
}
# PHP processing (if needed)
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
access_log /var/log/nginx/demo.example.com-access.log;
error_log /var/log/nginx/demo.example.com-error.log;
}
# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name demo.example.com;
return 301 https://$host$request_uri;
}
EOF
| ???? Note The second server block automatically redirects HTTP to HTTPS, ensuring all visitors use the secure connection. |
sudo ln -s /etc/nginx/sites-available/demo.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
What these commands do:
For more complex setups, you can customize how certificates are deployed after renewal. These advanced scenarios cover multiple servers, different services, and cloud integrations.
If you have different domains that need different services restarted, you can create a smart deployment script that handles each domain appropriately.
Use this when:
What this script does:
If you use a load balancer or have multiple web servers, you need to copy renewed certificates to all backend servers automatically.
Use this when:
Prerequisites:
What this script does:
???? Pro Tip: Setting Up SSH Keys To enable password-less copying, run this once on your main server: ssh-keygen (press Enter for all prompts) Then copy the key to each backend: ssh-copy-id root@server1.example.com |
If you use Microsoft Azure services, you can automatically upload renewed certificates to Azure Key Vault for use with Application Gateway, App Services, or other Azure resources.
Use this when:
Prerequisites:
az command)az login)What this script does:
Important customization:
mykeyvault with your actual Key Vault nameYourSecurePassword123! with a strong passwordCERT_NAME to match your certificate naming conventionRegular monitoring ensures your certificates stay valid and your site remains secure.
sudo certbot certificates
This shows all certificates managed by Certbot, including expiry dates. Example output:
Certificate Name: demo.example.com
Serial Number: 123456789abcdef
Domains: demo.example.com
Expiry Date: 2026-05-10 10:00:00+00:00 (VALID: 89 days)
Certificate Path: /etc/letsencrypt/live/demo.example.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/demo.example.com/privkey.pem
Check what Certbot has been doing:
sudo tail -f /var/log/letsencrypt/letsencrypt.log
This shows real-time activity from Certbot.
Use online tools to verify your SSL is properly configured:
| Tool | URL |
|---|---|
| SSL Labs Test | https://www.ssllabs.com/ssltest/ |
| Security Headers Check | https://securityheaders.com/ |
End of Certbot SSL Deployment Guide