Configure NGINX SSL certificate automation with Certbot and renewal monitoring

Intermediate 25 min Apr 30, 2026 1,035 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up automated SSL certificate provisioning and renewal for NGINX using Let's Encrypt Certbot with systemd timers, monitoring, and failure alerting for production environments.

Prerequisites

  • Root or sudo access
  • Domain name pointing to your server
  • NGINX web server
  • Internet connectivity for Let's Encrypt validation

What this solves

Manual SSL certificate management creates security risks and service interruptions when certificates expire unexpectedly. This tutorial automates SSL certificate provisioning, renewal, and monitoring for NGINX using Let's Encrypt Certbot with systemd timers, ensuring your web services maintain valid certificates without manual intervention.

Step-by-step configuration

Install NGINX and Certbot

Install NGINX web server and Certbot for Let's Encrypt certificate management.

sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
sudo dnf update -y
sudo dnf install -y nginx certbot python3-certbot-nginx

Configure NGINX virtual host

Create a basic NGINX virtual host configuration for your domain before requesting SSL certificates.

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }

    location /.well-known/acme-challenge/ {
        root /var/www/example.com;
        allow all;
    }
}

Create web root directory

Set up the web root directory with proper permissions for NGINX and Certbot validation.

sudo mkdir -p /var/www/example.com
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com
echo "<h1>Welcome to example.com</h1>" | sudo tee /var/www/example.com/index.html

Enable site and test configuration

Enable the virtual host and verify NGINX configuration syntax.

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo cp /etc/nginx/sites-available/example.com /etc/nginx/conf.d/example.com.conf
sudo nginx -t
sudo systemctl reload nginx

Request SSL certificate with Certbot

Use Certbot to request and automatically configure SSL certificates for your domain.

sudo certbot --nginx -d example.com -d www.example.com --email admin@example.com --agree-tos --no-eff-email
Note: Replace admin@example.com with your actual email address. This email receives expiration warnings as a backup to automated renewal.

Verify SSL certificate installation

Check that Certbot successfully modified your NGINX configuration with SSL settings.

sudo nginx -t
sudo systemctl reload nginx
sudo certbot certificates

Create certificate renewal script

Create a renewal script that handles certificate renewal and NGINX reloading with logging.

#!/bin/bash

# Certbot renewal script with logging and error handling
LOG_FILE="/var/log/certbot-renewal.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')

echo "[$DATE] Starting certificate renewal check" >> "$LOG_FILE"

# Attempt renewal
if /usr/bin/certbot renew --quiet --nginx >> "$LOG_FILE" 2>&1; then
    echo "[$DATE] Certificate renewal check completed successfully" >> "$LOG_FILE"
    
    # Test nginx configuration
    if /usr/sbin/nginx -t >> "$LOG_FILE" 2>&1; then
        # Reload nginx if config is valid
        /bin/systemctl reload nginx >> "$LOG_FILE" 2>&1
        echo "[$DATE] NGINX reloaded successfully" >> "$LOG_FILE"
    else
        echo "[$DATE] ERROR: NGINX configuration test failed" >> "$LOG_FILE"
        exit 1
    fi
else
    echo "[$DATE] ERROR: Certificate renewal failed" >> "$LOG_FILE"
    exit 1
fi

echo "[$DATE] Renewal process completed" >> "$LOG_FILE"

Make renewal script executable

Set proper permissions for the renewal script.

sudo chmod +x /usr/local/bin/certbot-renewal.sh
sudo chown root:root /usr/local/bin/certbot-renewal.sh

Create systemd service for renewal

Create a systemd service unit for certificate renewal.

[Unit]
Description=Certbot SSL Certificate Renewal
After=network.target

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/certbot-renewal.sh
StandardOutput=journal
StandardError=journal

Create systemd timer for automatic renewal

Configure a systemd timer to run certificate renewal checks twice daily.

[Unit]
Description=Run Certbot SSL renewal check twice daily
Requires=certbot-renewal.service

[Timer]
OnCalendar=*-*-* 06,18:00:00
RandomizedDelaySec=3600
Persistent=true

[Install]
WantedBy=timers.target

Enable and start the renewal timer

Enable the systemd timer to start automatically and begin running renewal checks.

sudo systemctl daemon-reload
sudo systemctl enable certbot-renewal.timer
sudo systemctl start certbot-renewal.timer
sudo systemctl status certbot-renewal.timer

Create certificate monitoring script

Create a monitoring script to check certificate expiration and send alerts.

#!/bin/bash

# Certificate monitoring script
WARN_DAYS=30
CRIT_DAYS=7
LOG_FILE="/var/log/cert-monitor.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')

# Function to check certificate expiration
check_cert() {
    local domain="$1"
    local exp_date
    local exp_epoch
    local now_epoch
    local days_until_exp
    
    exp_date=$(echo | openssl s_client -servername "$domain" -connect "$domain:443" 2>/dev/null | \
               openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
    
    if [ -z "$exp_date" ]; then
        echo "[$DATE] ERROR: Could not retrieve certificate for $domain" >> "$LOG_FILE"
        return 1
    fi
    
    exp_epoch=$(date -d "$exp_date" +%s)
    now_epoch=$(date +%s)
    days_until_exp=$(( (exp_epoch - now_epoch) / 86400 ))
    
    echo "[$DATE] Certificate for $domain expires in $days_until_exp days" >> "$LOG_FILE"
    
    if [ "$days_until_exp" -lt "$CRIT_DAYS" ]; then
        echo "[$DATE] CRITICAL: Certificate for $domain expires in $days_until_exp days!" >> "$LOG_FILE"
        logger -p daemon.crit "SSL Certificate CRITICAL: $domain expires in $days_until_exp days"
    elif [ "$days_until_exp" -lt "$WARN_DAYS" ]; then
        echo "[$DATE] WARNING: Certificate for $domain expires in $days_until_exp days" >> "$LOG_FILE"
        logger -p daemon.warning "SSL Certificate WARNING: $domain expires in $days_until_exp days"
    fi
}

# Check certificates for all configured domains
for cert_file in /etc/letsencrypt/live/*/cert.pem; do
    if [ -f "$cert_file" ]; then
        cert_dir=$(dirname "$cert_file")
        domain=$(basename "$cert_dir")
        check_cert "$domain"
    fi
done

Make monitoring script executable

Set permissions for the certificate monitoring script.

sudo chmod +x /usr/local/bin/cert-monitor.sh
sudo chown root:root /usr/local/bin/cert-monitor.sh

Create monitoring systemd service and timer

Set up systemd service and timer for certificate expiration monitoring.

[Unit]
Description=SSL Certificate Expiration Monitor
After=network.target

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/cert-monitor.sh
StandardOutput=journal
StandardError=journal
[Unit]
Description=Run SSL certificate monitoring daily
Requires=cert-monitor.service

[Timer]
OnCalendar=daily
RandomizedDelaySec=1800
Persistent=true

[Install]
WantedBy=timers.target

Enable certificate monitoring

Enable and start the certificate monitoring timer.

sudo systemctl daemon-reload
sudo systemctl enable cert-monitor.timer
sudo systemctl start cert-monitor.timer
sudo systemctl status cert-monitor.timer

Configure log rotation

Set up log rotation for certificate renewal and monitoring logs to prevent disk space issues.

/var/log/certbot-renewal.log /var/log/cert-monitor.log {
    weekly
    rotate 12
    compress
    delaycompress
    missingok
    notifempty
    create 644 root root
}

Verify your setup

Test your SSL certificate automation and monitoring configuration.

# Check SSL certificate status
sudo certbot certificates

# Test renewal process (dry run)
sudo certbot renew --dry-run

# Check systemd timers
sudo systemctl list-timers | grep cert

# Test certificate monitoring
sudo /usr/local/bin/cert-monitor.sh

# Check logs
sudo tail -f /var/log/certbot-renewal.log
sudo tail -f /var/log/cert-monitor.log

# Test SSL configuration
curl -I https://example.com
openssl s_client -connect example.com:443 -servername example.com

Common issues

Symptom Cause Fix
Certbot validation fails DNS not pointing to server or firewall blocking port 80 Verify DNS with dig example.com and check firewall rules
NGINX fails to reload after renewal Invalid NGINX configuration Test config with sudo nginx -t and fix syntax errors
Certificate renewal timer not running Timer not enabled or systemd issues Check with sudo systemctl status certbot-renewal.timer
Monitoring script shows SSL errors Certificate not properly installed or expired Re-run Certbot with sudo certbot --nginx -d example.com
Log files growing too large Logrotate not configured properly Test logrotate with sudo logrotate -d /etc/logrotate.d/certbot-logs

Next steps

Running this in production?

Want this handled for you? Setting this up once is straightforward. Keeping it patched, monitored, backed up and performant across environments is the harder part. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

不想自己管理这些吗?

我们为依赖稳定运行时间的企业管理基础设施。全托管服务,配备一位熟悉您系统架构的固定联系人。

您将拥有一位了解您整体架构的固定联系人

Rotterdam 06:08 · 一条消息即可联系我们,无需填写工单表单