924 words
5 minutes
Automating Certificate Renewal with Let's Encrypt and Ansible

Introduction#

Maintaining valid SSL/TLS certificates is crucial for securing web applications and ensuring user trust. Let’s Encrypt provides free SSL/TLS certificates, but they are only valid for 90 days, requiring regular renewal. Manually renewing certificates can be tedious and error-prone. This article demonstrates how to automate Let’s Encrypt certificate renewal using Ansible, a powerful automation tool. This approach ensures your certificates are always up-to-date, minimizing the risk of downtime and security vulnerabilities.

Prerequisites#

Before you begin, ensure you have the following:

  • An Ansible control node with Ansible installed (version 2.9 or later is recommended).
  • Access to the target servers where the certificates will be renewed. These servers should be accessible via SSH from the Ansible control node.
  • A working web server (e.g., Apache, Nginx) configured to use Let’s Encrypt certificates.
  • certbot installed on the target servers.
  • Basic familiarity with Ansible playbooks and concepts.

Understanding the Workflow#

The automated certificate renewal process will involve the following steps:

  1. Check Certificate Expiry: Determine if the existing certificate is nearing its expiration date.
  2. Renew Certificate: If the certificate is about to expire, trigger the certbot renew command.
  3. Reload Web Server: Restart or reload the web server to use the newly renewed certificate.

Creating the Ansible Playbook#

Create an Ansible playbook named renew_certs.yml with the following content:

---
- hosts: webservers
  become: true
  tasks:
    - name: Check certificate expiry date
      shell: "certbot certificates | grep 'Expiry Date:' | awk '{print $3}'"
      register: expiry_date
      changed_when: false

    - name: Calculate days until expiry
      shell: "date -d \"${{ expiry_date.stdout }}\" +%s && date +%s"
      register: expiry_timestamp
      changed_when: false

    - name: Set expiry fact
      set_fact:
        expiry_days: "{{ (expiry_timestamp.stdout_lines[0].split(' ')[0] | int - expiry_timestamp.stdout_lines[0].split(' ')[1] | int) / 86400 }}"

    - name: Renew certificate if expiry is less than 30 days
      shell: certbot renew
      when: expiry_days | int < 30
      notify: reload webserver

  handlers:
    - name: reload webserver
      service:
        name: apache2 # Replace with your web server service name (e.g., nginx)
        state: reloaded

Explanation:

  • hosts: webservers: Specifies the target hosts. You’ll need to define the webservers group in your Ansible inventory file.
  • become: true: Executes the tasks with elevated privileges (root).
  • tasks: Defines the sequence of actions to be performed.
    • Check certificate expiry date: Executes certbot certificates to retrieve the certificate information and extracts the expiry date using awk. The output is registered in the expiry_date variable. changed_when: false prevents Ansible from reporting a change if the command output is the same.
    • Calculate days until expiry: Calculates the number of days until the certificate expires by comparing the expiry date with the current date using date. The output is registered in the expiry_timestamp variable. changed_when: false prevents Ansible from reporting a change if the command output is the same.
    • Set expiry fact: Calculates the difference in days between the expiry date and the current date and stores it in the expiry_days fact.
    • Renew certificate if expiry is less than 30 days: Executes certbot renew to renew the certificate if the expiry_days is less than 30. The when condition ensures that the renewal is only triggered when necessary. This task also includes a notify directive that triggers the reload webserver handler.
  • handlers: Defines actions to be performed in response to events triggered by the tasks.
    • reload webserver: Reloads the web server service (Apache in this example). Replace apache2 with the appropriate service name for your web server (e.g., nginx). Reloading the web server gracefully applies the new certificate without interrupting existing connections.

Configuring the Ansible Inventory#

Create or modify your Ansible inventory file (e.g., hosts) to include the webservers group and the IP addresses or hostnames of your web servers:

[webservers]
webserver1.example.com
webserver2.example.com

Replace webserver1.example.com and webserver2.example.com with the actual hostnames or IP addresses of your web servers.

Running the Playbook#

Execute the playbook using the following command:

ansible-playbook renew_certs.yml -i hosts

Replace hosts with the path to your Ansible inventory file if it’s not in the default location.

Ansible will connect to each server in the webservers group, check the certificate expiry date, and renew the certificate if necessary. If a certificate is renewed, the web server will be reloaded.

Scheduling the Playbook#

To automate the certificate renewal process, schedule the playbook to run regularly using cron. Add the following line to your crontab (using crontab -e):

0 3 * * * ansible-playbook /path/to/renew_certs.yml -i /path/to/hosts

This will run the playbook every day at 3:00 AM. Adjust the schedule as needed. Ensure the user running the cron job has the necessary permissions to execute the Ansible playbook and access the target servers.

Enhancements and Considerations#

  • Error Handling: Add error handling to the playbook to gracefully handle potential issues, such as network connectivity problems or certbot failures. You can use the ignore_errors: true directive and the failed_when condition to customize error handling.
  • Certificate Storage: Consider using a centralized certificate storage solution, such as HashiCorp Vault, to manage and distribute certificates across multiple servers.
  • DNS Challenges: If you’re using DNS challenges for certificate validation, ensure that the Ansible playbook has the necessary permissions to update your DNS records.
  • Testing: Thoroughly test the playbook in a non-production environment before deploying it to production. You can use the --check and --diff flags to preview the changes that Ansible will make.
  • Logging: Implement logging to track the execution of the playbook and identify any potential issues. You can use the log_plays setting in your Ansible configuration file to enable logging.

Conclusion#

Automating Let’s Encrypt certificate renewal with Ansible simplifies the process of maintaining valid SSL/TLS certificates and ensures continuous HTTPS security for your web applications. By scheduling the playbook to run regularly, you can minimize the risk of certificate expiration and downtime. This approach enhances security, reduces manual effort, and improves the overall reliability of your web infrastructure. Remember to tailor the playbook to your specific environment and thoroughly test it before deploying it to production.

Automating Certificate Renewal with Let's Encrypt and Ansible
https://en.dymripper.com/posts/2025-05-18-automating-certificate-renewal-with-lets-encrypt-and-ansible/
Author
DYMripper
Published at
2025-05-18