Let’s Encrypt: How to Automatically Restart Nginx With Certbot
Let’s Encrypt has steadily improved since its public debut in late 2015. Certbot, the most popular Let’s Encrypt client, is available for a wide variety of Linux distributions, making it easy to integrate Let’s Encrypt with many common web server configurations. However, because of this broad support, and because Certbot offers many internal options, there are several different ways to integrate Certbot with Nginx.
If you run Certbot with the --nginx
flag, it will automatically make whatever changes are necessary to your Nginx configuration to enable SSL/TLS for your website. On the other hand, if you’d prefer to handle the Nginx configuration separately, you can run Certbot with the --webroot
flag. In this mode, Certbot will still fetch a certificate, but it’s up to you to integrate it with Nginx.
Once you’ve obtained certificates from Let’s Encrypt, you’ll need to set up a method to automatically renew them, since they expire after just 90 days. On Ubuntu 18.04, the “certbot” package from the Ubuntu repositories includes an automatic renewal framework right out of the box. However, you’ll also need to reload your web server so it can actually serve the renewed certificates. The packaged renewal scripts on Ubuntu won’t restart Nginx unless you used the --nginx
flag to request certificates in the first place. If you’re using --webroot
or some other method, there’s an additional important step to take.
Automatically Restarting Nginx
On Ubuntu 18.04, Certbot comes with two automated methods for renewing certificates: a cron job, located at /etc/cron.d/certbot
, and a systemd timer. The cron job is set to run every 12 hours but only takes effect if systemd is not active. Instead, the systemd timer (visible in the output of systemctl list-timers
) works in tandem with the certbot
systemd service to handle certificate renewals.
Instead of modifying the cron job or the systemd service, we can change Certbot’s renewal behavior by editing a config file. Add the following line to /etc/letsencrypt/cli.ini
:
deploy-hook = systemctl reload nginx
This will cause Certbot to reload Nginx after it renews a certificate. With the deploy-hook
option, Certbot will only reload Nginx when a certificate is actually renewed, not every time the Certbot renewal check runs. Ed: A previous version of this post recommended using renew-hook
instead. This option has been superseded by deploy-hook
.
You can verify that your changes are working by running certbot renew --dry-run
. This will not renew any certificates but will tell you if your deploy-hook
command is being picked up by Certbot.
A Little Background Information
If you’re new to Let’s Encrypt, and you’re wondering why you need to automatically renew your certificates and restart your web server when you get new ones, it’s a good thing you’re here. While “traditional” SSL/TLS certificates are manually requested and can be valid for up to two years, certificates from Let’s Encrypt are only valid for 90 days. In their blog post, the Let’s Encrypt team explains their reasoning behind such short certificate lifetimes: they limit the time period for damage to be caused by stolen keys or mis-issued certificates, and they heavily encourage automation, which is key to the success of the Let’s Encrypt model.
This means that you’re going to need to automatically renew your certificates in order to take full advantage of Let’s Encrypt. Fortunately, since this is how Let’s Encrypt is designed to work, auto-renewal functionality is built directly into Certbot, the recommended ACME client for Let’s Encrypt.
A slightly less obvious question is why you’d want to automatically restart your web server as well. The answer is simple: web servers, such as Apache or Nginx, don’t read your SSL/TLS certificates directly from disk every time they need them. Instead, they load them into memory along with the rest of the web server configuration. This is great, and perfectly normal, since reading the certificates from disk would be horribly inefficient. However, it means that updating (or renewing) a certificate with Let’s Encrypt won’t directly change the certificate that Apache/Nginx serves when a page is requested. Instead, the web server must be restarted in order to load the new certificate into memory.
You read all my mind. kudos.
Thanks for this article. Saved my butt.
The dry run gives me:
Dry run: skipping deploy hook command: systemctl reload nginx
Is that OK? Other sources mean, that this is intentionally skipped in dry run, so it will be executed on renewal
This is OK. Per the certbot man page, "–dry-run" does not call "–deploy-hook" commands. (It does call "–pre-hook" and "–post-hook" commands, however.)
Thanks man this helped me out a lot!
Peace
Deploy-hook runs after each separate certificate renewal, to reload nginx you need to use post-hook. My advice is to use the following line instead, in /etc/letsencrypt/cli.ini:
post-hook = systemctl reload nginx
My logic for using "deploy-hook" rather than "post-hook" is that "deploy-hook" only runs the hook after a successful renewal. If the certificate renewal fails, I would rather not reload Nginx, as it could potentially pick up a broken certificate that way.
Based on the Certbot docs, I believe the two options are otherwise identical.
Incredibly helpful. Thank you.
Perhaps a test of the nginx configuration could be added. Perhaps doing this way might work:
deploy-hook = nginx -t && systemctl reload nginx
The "nginx -t && …" construct will make certbot assume there is an error, probably because it prints the lines that the configuration files are okay.
Because the nginx configuration does not get changed by certbot, there is little use in validating the nginx configuration after each renewal.
I would advice to use
/etc/letsencrypt/renewal/your.domain.conf
and use a line like
renew_hook = systemctl try-reload-or-restart servicename
Why? Now each certificate restarts its specific service. Otherwise each renewal triggers the reload of all services (if you have configured more than one).
Note that the keyword chosen by certbot itself is renew_hook, even though it behaves like deploy-hook.
And try-reload-or-restart (instead of reload) is also more general: it restarts if no reload is available for the service and does not even do that if the service is disabled.
So: the advice from the OP is not wrong, but my variation is perfect in this case, and in many more cases as well.
services (in case you have
Thanks, Robert — this is good advice.
Hello!
I made the changes according to the instructions and it seems to me that the changes I made are working! 🙂 Could you look at this picture so you also think I'm okay?
Picture link: https://aijaa.com/RkPWUN
Thanks!
Looks good to me.
is the reload working well ? I hit several time an issue having nginx NOT using the new certificate after a reload. a restart fix it.
That's interesting. I've updated the post to recommend using "deploy-hook" over "renew-hook", but that should not change whether or not "systemctl reload nginx" works. I'm using "systemctl reload nginx" in production and will update the post if I catch it failing to load updated certificates.
This was super handy, thank you. Looking at the directory structure, I thought I might have to create a file in `/etc/letsencrypt/renewal-hooks/(post|deploy)/`, but I didn't know which directory was required and what that file might have to look like. I'm glad to know that I just needed to edit an entirely different file. Thank you!
Thank you!! Certbot keeps changing the install instructions and I keep trying to keep up, but inevitably my SSL certs keep crapping out every 3 months. I hope this does it once and for all. Very helpful explanation.
This is exactly what I needed to know! I couldn't understand why my websites kept serving "expired" certificates.
At first, I thought certbot was failing to renew the certificates. But then I ran `sudo certbot renew –dry-run` and it said "no certificates are due for renewal". That let me know that the certificates ARE being renewed, but not served.
Then I saw a comment somewhere on certbot's github that the web server nginx must be restarted after renewal. Finally I found you page and got the explanation I needed. Thank you! I didn't know that nginx served the SSL cert from memory and didn't read from disk upon renewal. Your solution of adding it into the cli.ini file is brilliant.
Thanks again!