Set up Certbot for Nginx on Ubuntu 18.04

How to set up Certbot for Nginx on Ubuntu 18.04, including automated renewals via systemd.

The EFF’s free certificate service, Certbot, has greatly simplified the task of setting up HTTPS for your websites. The only downside is that the certificates are only good for 90 days. Fortunately renewing is easy, and we can even automate it all with systemd. Here’s how to set up Certbot with Nginx and make sure your SSL certs renew indefinitely with no input from you.

This tutorial is aimed at anyone using an Ubuntu 18.04 VPS from cheap hosts like DigitalOcean or Vultr.com, but should also work for other versions of Ubuntu, Debian, Fedora, CentOS and any other system that uses systemd. The only difference will be the commands you use to install Certbot. See the Certbot site for instructions specific to your system.

First we’ll get Certbot running on Ubuntu 18.04, then we’ll dive into setting up automatic renewals via systemd.

You should not need this with 18.04, but to be on the safe side, make sure you have the software-properties-common package installed.

sudo apt install software-properties-common

The next part requires that you add a PPA, my least favorite part of Certbot for Ubuntu, as I don’t like to rely on PPAs for something as mission critical as my security certificates. Still, as of this writing, there is not a better way. At least go look at the code before you blindly cut and paste. When you’re done, here’s your cut and paste:

sudo apt update
sudo add-apt-repository ppa:certbot/certbot
sudo apt update
sudo apt install python-certbot-nginx 

Now you’re ready to install some certs. For this part I’m going to show the commands and the output of the commands since the certbot command is interactive. Note that the version below will append some lines to your Nginx config file. If you prefer to edit your config file yourself, use this command: sudo certbot --nginx certonly, otherwise, here’s what it looks like when you run sudo certbot --nginx:

sudo certbot --nginx

[sudo] password for $youruser: 
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx 

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: luxagraf.net
2: awstats.luxagraf.net
3: origin.luxagraf.net
4: www.luxagraf.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -                                                                                                                                                               
Select the appropriate numbers separated by commas and/or spaces, or leave input blank to select all options shown (Enter 'c' to cancel): 4
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for www.luxagraf.net
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/luxagraf.net.conf

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Traffic on port 80 already redirecting to ssl in /etc/nginx/sites-enabled/luxagraf.net.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://www.luxagraf.net.
You should test your configuration at: https://www.ssllabs.com/ssltest/analyze.html?d=www.luxagraf.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/www.luxagraf.net/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/www.luxagraf.net/privkey.pem
   Your cert will expire on 2019-01-09. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - If you like Certbot, please consider supporting our work by:
   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le     

And there you have it, SSL certs for all your domains.

Unfortunately, those new certs are only good for 90 days. The odds of you remembering to renew that every 90 days — even with reminder emails from the EFF — is near nil. Plus, do you really want to be renewing certs by hand, like an animal? No, you want to automate everything so you can do better things with your time.

You could use cron, but the more modern approach would be to create a systemd service and a systemd timer to control when that service runs.

I highly recommend reading through the Arch Wiki page on systemd services and timers, as well as the systemd.timer man pages to get a better understanding of how you can automate other tasks in your system. But for the purposes of this tutorial all you really need to understand is that timers are just like other systemd unit files, but they include a [Timer] block which lets you specify exactly when you want your service file to run.

Timer files can live right next to your service files in /etc/systemd/system/.

There’s no hard and fast rules about naming timers, but it makes sense to use the same name as the service file the timer controls, except the timer gets the .timer extension. So you’d have two files myservice.service and myservice.timer.

Let’s start with the service file. I call mine certbot-renewal. Open the service file:

sudo nano /etc/systemd/system/certbot-renewal.service

This is going to be a super simple service, we’ll give it a description and a command to run and that’s it:

[Unit]
Description=Certbot Renewal

[Service]
ExecStart=/usr/bin/certbot renew

Next we need to create a .timer file that will run the certbot.renewal service every day. Create this file:

sudo nano /etc/systemd/system/certbot-renewal.timer

And now for the slightly more complex timer:

[Unit]
Description=Certbot Renewal Timer

[Timer]
OnBootSec=500
OnUnitActiveSec=1d

[Install]
WantedBy=multi-user.target

The [Timer] directive can take a number of parameters, the ones we’ve used constitute what’s called a monotonic timer, which means they run “after a time span relative to a varying starting point”. In other words they’re not calendar events like cron.

Our monotonic timer has two directives, onBootSec and OnUnitActiveSec. The first should be obvious, our timer will run 500 seconds after the system boots. Why 500? No real reason, I just didn’t want to bog down the system at boot.

The OnUnitActiveSec is really what makes this work. This directive measures time relative to when the service that the timer controls was last activated. In our case the 1d means run the service one day after it last ran. So our timer will run once a day to make sure our scripts stay up to date.

As a kind of footnote, in systemd parlance calendar-based timers are called realtime timers and can be used to replace cron if you want. There are some disadvantages, see the Arch Wiki for a good overview of what you get and what you lose if you go that route.

Okay, the last step for our certbot renewal system is to enable and then start our timer. Note that we don’t have to do either to our actual service file because we don’t want it active, the timer will control when it runs.

sudo systemctl enable certbot-renewal.timer
sudo systemctl start certbot-renewal.timer

Run those commands and you’re done. Your timer is now active and your Certbot certificates will automatically renew as long as your server is up and running.