Nginx SSL certificate renewal automation guide
What You’ll Need
- n8n Cloud or self-hosted n8n
- Hetzner VPS or Contabo VPS for hosting your Nginx server
- DigitalOcean as an alternative hosting provider
- Linux server with Nginx installed (Ubuntu 20.04 LTS or newer recommended)
- Certbot installed on your server
- SSH access to your server
- A domain name (get one from Namecheap if needed)
Table of Contents
- Understanding SSL Certificate Lifecycle Management
- Setting Up Certbot for Automated Renewals
- Automating Notifications with n8n
- Complete Renewal Workflow Configuration
- Monitoring and Troubleshooting
- Getting Started
Understanding SSL Certificate Lifecycle Management
SSL certificates expire. That’s a fact that catches too many DevOps engineers off guard, especially when you’re managing dozens of domains. Let me walk you through a real scenario: you’ve got a production Nginx server running critical APIs, and your SSL certificate silently expires on a Friday afternoon. Browsers start warning users, monitoring dashboards light up red, and you’re scrambling to renew everything manually while customers report errors.
I’ve been there. The solution is automation, and it’s far simpler than most people think.
When you use Let’s Encrypt with Certbot, your certificates are valid for 90 days. The renewal window typically opens at day 30, giving you 60 days to renew before expiration. If you automate this process, you’ll never miss a deadline. The typical flow involves running Certbot on your Nginx server, validating domain ownership through the ACME challenge, and reloading your Nginx configuration.
The key insight is that certificate renewal doesn’t need human intervention. With proper automation, you can:
- Check certificate expiration dates automatically
- Trigger renewals when certificates approach expiration
- Validate the renewal succeeded
- Reload Nginx with the new certificates
- Send notifications to your team
- Log everything for audit purposes
Setting Up Certbot for Automated Renewals
Before diving into n8n Cloud orchestration, you need a solid foundation on your server. I’m assuming you’ve already got Nginx running on your Hetzner VPS or similar Linux environment.
First, install Certbot and the Nginx plugin:
apt-get update
apt-get install -y certbot python3-certbot-nginx
If you already have certificates issued, skip this. If not, issue one:
certbot certonly --nginx -d yourdomain.com -d www.yourdomain.com -n --agree-tos -m your-email@example.com
Replace yourdomain.com with your actual domain. The -n flag runs non-interactively, essential for automation.
Now here’s the critical part: set up a renewal hook that reloads Nginx after a successful renewal. Create a file at /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh:
#!/bin/bash
nginx -t && systemctl reload nginx
Make it executable:
chmod +x /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
The renewal hook ensures that after Certbot updates your certificate files, Nginx gets reloaded to use the new certificates. Without this step, you’d have valid certificates on disk but Nginx would still serve the old ones.
Test the renewal process manually first:
certbot renew --dry-run
This runs through the entire renewal process without actually modifying your certificates. Check for errors in the output. The typical success message looks like this:
The dry run was successful.
Now set up the cron job for automatic renewals. Certbot includes a default cron job at /etc/cron.d/certbot, but I prefer explicit control. Create /etc/cron.d/certbot-renewal:
0 2 * * * root certbot renew --quiet --renew-hook /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
This runs daily at 2 AM, checks if any certificates need renewal, and reloads Nginx if updates occur.
Automating Notifications with n8n
Here’s where things get interesting. While your server handles certificate renewal automatically, you need visibility into what’s happening. This is where n8n Cloud comes in.
I’ll show you how to set up an n8n workflow that checks certificate expiration dates daily and notifies your team when certificates are approaching expiration or if renewals fail.
The workflow needs three core pieces:
- A daily trigger
- SSH execution to query certificate status
- Conditional notifications based on days remaining
Let me break down the complete workflow configuration. Start by creating a new workflow in n8n called “SSL Certificate Health Check”.
First, add a Schedule trigger node set to run daily at 3 AM (right after your renewal cron job completes):
{
"name": "Schedule",
"type": "n8n-nodes-base.schedule",
"position": [250, 300],
"typeVersion": 1,
"parameters": {
"rule": {
"interval": [1],
"intervalUnit": "days",
"hours": 3,
"minutes": 0
}
}
}
Next, add an SSH Execute node to query your certificates. This node connects to your Nginx server via SSH and extracts certificate expiration dates:
{
"name": "Get Certificate Expiry",
"type": "n8n-nodes-base.ssh",
"position": [450, 300],
"typeVersion": 1,
"parameters": {
"command": "openssl x509 -enddate -noout -in /etc/letsencrypt/live/yourdomain.com/cert.pem | cut -d= -f2",
"authentication": "privateKey",
"host": "your-server-ip",
"username": "root",
"privateKey": "YOUR_PRIVATE_KEY_HERE",
"port": 22
}
}
The command above extracts just the expiration date. You’ll see output like:
Dec 18 10:43:23 2025 GMT
Now add another SSH node to check all your domains. For multiple domains, you might loop through them:
{
"name": "Get All Certificate Dates",
"type": "n8n-nodes-base.ssh",
"position": [650, 300],
"typeVersion": 1,
"parameters": {
"command": "for domain in yourdomain.com anotherdomain.com thirddomain.com; do echo -n \"$domain: \"; openssl x509 -enddate -noout -in /etc/letsencrypt/live/$domain/cert.pem | cut -d= -f2; done",
"authentication": "privateKey",
"host": "your-server-ip",
"username": "root",
"privateKey": "YOUR_PRIVATE_KEY_HERE",
"port": 22
}
}
This outputs:
yourdomain.com: Dec 18 10:43:23 2025 GMT
anotherdomain.com: Dec 20 14:15:47 2025 GMT
thirddomain.com: Dec 15 08:22:11 2025 GMT
Add a Function node to parse these dates and calculate days remaining:
{
"name": "Parse Certificate Dates",
"type": "n8n-nodes-base.function",
"position": [850, 300],
"typeVersion": 1,
"parameters": {
"functionCode": "const output = $input.all()[0].json.output;\nconst lines = output.split('\\n').filter(line => line.trim());\nconst today = new Date();\nconst certificates = [];\n\nlines.forEach(line => {\n const [domain, dateStr] = line.split(': ');\n if (domain && dateStr) {\n const expiryDate = new Date(dateStr);\n const daysRemaining = Math.floor((expiryDate - today) / (1000 * 60 * 60 * 24));\n certificates.push({\n domain: domain.trim(),\n expiryDate: expiryDate.toISOString(),\n daysRemaining: daysRemaining,\n isWarning: daysRemaining <= 30,\n isCritical: daysRemaining <= 7\n });\n }\n});\n\nreturn [{json: {certificates: certificates}}];"
}
}
This function returns structured data with warning flags based on how many days remain.
💡 Fast-Track Your Project: Don’t want to configure this yourself? I build custom n8n pipelines and bots. Message me with code SYS3-HUGO.
Complete Renewal Workflow Configuration
Now let’s tie it all together. After the function node parses the certificate data, you need conditional notifications.
Add an If node that checks if any certificate is in warning state:
{
"name": "Check Certificate Status",
"type": "n8n-nodes-base.if",
"position": [1050, 300],
"typeVersion": 1,
"parameters": {
"conditions": {
"condition1": {
"value1": "={{ $node[\"Parse Certificate Dates\"].json.certificates.some(cert => cert.isWarning) }}",
"operation": "equals",
"value2": true
}
},
"combineOperation": "any"
}
}
If the condition is true (meaning at least one certificate needs attention), send a notification. Add an Email node:
{
"name": "Send Email Alert",
"type": "n8n-nodes-base.emailSendGmail",
"position": [1250, 250],
"typeVersion": 1,
"parameters": {
"fromEmail": "your-email@gmail.com",
"toEmail": "devops-team@yourcompany.com",
"subject": "SSL Certificate Renewal Alert",
"emailBody": "Certificate Status Report:\\n\\n{{ $node[\"Parse Certificate Dates\"].json.certificates.map(cert => `${cert.domain}: ${cert.daysRemaining} days remaining - ${ cert.isCritical ? 'CRITICAL' : cert.isWarning ? 'WARNING' : 'OK' }`).join('\\n') }}"
}
}
For critical situations (certificates expiring in 7 days or less), add a second conditional node and send an additional Slack notification:
{
"name": "Check Critical Status",
"type": "n8n-nodes-base.if",
"position": [1050, 450],
"typeVersion": 1,
"parameters": {
"conditions": {
"condition1": {
"value1": "={{ $node[\"Parse Certificate Dates\"].json.certificates.some(cert => cert.isCritical) }}",
"operation": "equals",
"value2": true
}
},
"combineOperation": "any"
}
}
{
"name": "Send Slack Critical Alert",
"type": "n8n-nodes-base.slack",
"position": [1250, 450],
"typeVersion": 1,
"parameters": {
"channel": "#devops-critical",
"text": "🚨 CRITICAL: SSL certificates expiring within 7 days!\\n\\n{{ $node[\"Parse Certificate Dates\"].json.certificates.filter(cert => cert.isCritical).map(cert => `• ${cert.domain}: ${cert.daysRemaining} days`).join('\\n') }}"
}
}
You need to connect your Slack workspace to n8n first. In your n8n settings, create a new Slack credential with your workspace’s bot token.
Finally, add a logging node to track all checks, even when there are no issues:
{
"name": "Log Check Result",
"type": "n8n-nodes-base.function",
"position": [1400, 300],
"typeVersion": 1,
"parameters": {
"functionCode": "const fs = require('fs');\nconst timestamp = new Date().toISOString();\nconst logEntry = `${timestamp} - Certificate check completed. Status: ${ $node[\"Parse Certificate Dates\"].json.certificates.map(c => `${c.domain}(${c.daysRemaining}d)`).join(', ') }\\n`;\n\nconsole.log(logEntry);\n\nreturn [{json: {logged: true}}];"
}
}
Monitoring and Troubleshooting
Deploy your workflow and let it run for a few days. Watch the execution history to ensure it’s working properly.
Common issues you might encounter:
SSH connection timeouts: Make sure your n8n server has network access to your Nginx server. Check firewall rules allowing port 22. If using n8n Cloud, you’ll need to whitelist their IP ranges or use a self-hosted instance on your Contabo VPS.
Certificate file not found: If Certbot hasn’t completed its first renewal yet, the certificate paths might not exist. Ensure your initial certificate issuance succeeded and files are at /etc/letsencrypt/live/yourdomain.com/.
Nginx reload failing: Test your renewal hook manually: bash /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh. Check for syntax errors in your Nginx config.
Missing private key in n8n: The SSH node requires your server’s private key. Generate one if you don’t have it: ssh-keygen -t ed25519. Copy the public key to ~/.ssh/authorized_keys on your server.
For deeper insight into logs, check Certbot’s log file on your server:
tail -f /var/log/letsencrypt/letsencrypt.log
This shows exactly what Certbot is doing during renewal attempts.
If you’re managing complex certificate scenarios where you parse automated ticket data or handle webhook events, you might benefit from building production-grade data pipelines. For reference, similar orchestration principles apply to scenarios like building production web scraping pipelines with Python, where you’re scheduling tasks and parsing structured data.
Getting Started
Let’s recap the complete setup:
- Install Certbot on your Hetzner VPS or DigitalOcean server
- Create renewal hooks to reload Nginx
- Test renewal with
certbot renew --dry-run - Set up the cron job for daily checks
- Build the n8n workflow for monitoring and notifications
- Deploy n8n Cloud or self-host on your infrastructure
The automation cycle will run independently. Your Nginx server renews certificates automatically every 60 days, and n8n keeps you informed of the status. If something goes wrong, you’ll know immediately instead of discovering it when users start seeing browser warnings.
This approach scales well. Whether you’re managing 3 domains or 30, the same workflow handles them all. Just add more domains to the SSH query in the “Get All Certificate Dates” node.
For additional monitoring capabilities, if you want to extend this to track certificate validation metrics or parse support tickets about SSL issues, the principles used here align with concepts in parsing inbound support tickets with OpenAI. You could feed certificate warnings into your ticket system automatically.
Outsource Your Automation
Don’t have time? I build production n8n workflows, WhatsApp bots, and fully automated YouTube Shorts pipelines. Hire me on Fiverr, mention SYS3-HUGO for priority. Or DM at chasebot.online.
Want to automate this yourself?
Start with n8n Cloud (free tier available) or self-host on a Hetzner VPS for full control.
Want this engine running on your own VPS?
This blog publishes itself — daily, unattended, on free API tiers. The full engine, Hugo theme, and setup guide are available as System 3.
Get System 3