Let’s Encrypt provides free, automated TLS certificates that are trusted by all major browsers. Certbot is the official ACME client that handles certificate issuance and renewal against the Let’s Encrypt CA. On RHEL 9, Certbot is available through the EPEL repository, making integration with Nginx and Apache straightforward. This tutorial walks through installing Certbot, obtaining certificates, and configuring fully automated renewals using systemd timers.
Prerequisites
- RHEL 9 server with a public IP address
- A registered domain name with DNS A records pointing to the server
- Nginx or Apache installed and serving HTTP traffic
- Port 80 and 443 open in firewalld
- EPEL repository enabled (
dnf install -y epel-release)
Step 1 — Install Certbot and the Web Server Plugin
With EPEL enabled, install Certbot and the appropriate plugin for your web server. The Nginx plugin automates the configuration of TLS directives, while the Apache plugin performs similar tasks for httpd. Install both here so you can choose based on your stack.
dnf install -y certbot python3-certbot-nginx python3-certbot-apache
Verify the installation:
certbot --version
Step 2 — Obtain a Certificate Using the Nginx Plugin
Run Certbot with the --nginx flag to automatically obtain and install the certificate. Replace example.com with your actual domain. Certbot will modify your Nginx configuration to add the necessary TLS directives and redirect HTTP to HTTPS.
certbot --nginx -d example.com -d www.example.com
You will be prompted for an email address for renewal notices and asked to agree to the Let’s Encrypt Terms of Service. After successful issuance, Certbot updates your Nginx server block and reloads the service. For Apache, substitute --apache for --nginx.
Step 3 — Understand the /etc/letsencrypt/ Directory Structure
Certbot stores all certificate files and account data under /etc/letsencrypt/. The key subdirectories are:
live/example.com/— symlinks to the current active certificate set:cert.pem,chain.pem,fullchain.pem,privkey.pemarchive/example.com/— all past and current certificate files by version numberrenewal/example.com.conf— renewal configuration including the authenticator plugin and domain listaccounts/— ACME account keys for each CA endpoint
ls -la /etc/letsencrypt/live/example.com/
cat /etc/letsencrypt/renewal/example.com.conf
Step 4 — Verify Automatic Renewal with the Systemd Timer
RHEL 9’s Certbot package ships with a systemd timer that runs certbot renew twice daily. Certificates are only renewed when they are within 30 days of expiry. Check that the timer is active:
systemctl status certbot.timer
systemctl list-timers --all | grep certbot
If the timer is not running, enable and start it:
systemctl enable --now certbot.timer
Simulate a renewal to confirm the configuration is correct without contacting the Let’s Encrypt servers:
certbot renew --dry-run
Step 5 — Configure Pre and Post Renewal Hooks
Renewal hooks allow you to run scripts before and after certificate renewal. Post-hooks are the most common use case — they reload the web server after a new certificate is installed. Create a hook script in the Certbot hooks directory:
cat > /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh << 'EOF'
#!/bin/bash
systemctl reload nginx
EOF
chmod +x /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
Pre-hooks (in renewal-hooks/pre/) run before renewal and can be used to temporarily stop a service if needed. Certbot will only execute the post-hook if the certificate was actually renewed.
Step 6 — Standalone Mode and Wildcard Certificates
For servers without a running web server, standalone mode temporarily starts a built-in HTTP server on port 80 to complete the ACME challenge. Stop any service using port 80 first:
systemctl stop nginx
certbot certonly --standalone -d server.example.com
systemctl start nginx
Wildcard certificates (e.g., *.example.com) require a DNS-01 challenge because Let’s Encrypt must verify control of the entire domain. Install the appropriate DNS plugin for your provider (e.g., python3-certbot-dns-cloudflare) and configure credentials, then issue:
certbot certonly
--dns-cloudflare
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini
-d "*.example.com"
-d example.com
Store DNS provider credentials in a file with permissions 600 to protect them from other users.
Conclusion
You have installed Certbot on RHEL 9, obtained a TLS certificate from Let’s Encrypt, explored the certificate directory structure, confirmed that the systemd renewal timer is active, and configured reload hooks. Your certificates will now renew automatically without manual intervention, keeping your server secure with current TLS credentials.
Next steps: How to Configure IPv6 Dual-Stack Networking on RHEL 9, How to Harden Nginx TLS Configuration on RHEL 9, and How to Set Up HAProxy with TLS Termination on RHEL 9.