Skip to content

Instantly share code, notes, and snippets.

Last active April 18, 2023 05:01
Show Gist options
  • Save xrstf/581981008b6be0d2224f to your computer and use it in GitHub Desktop.
Save xrstf/581981008b6be0d2224f to your computer and use it in GitHub Desktop.
Let's Encrypt on Ubuntu 14.04, nginx with webroot auth

Let's Encrypt on Ubuntu 14.04, nginx with webroot auth

This document details how I setup LE on my server. Firstly, install the client as described on and make sure you can execute it. I put it in /root/letsencrypt.

As it is not possible to change the ports used for the standalone authenticator and I already have a nginx running on port 80/443, I opted to use the webroot method for each of my domains (note that LE does not issue wildcard certificates by design, so you probably want to get a cert for and


For this, I placed config files into etc/letsencrypt/configs, named after <domain>.conf. The files are simple:

# the domain we want to get the cert for;
# technically it's possible to have multiple of this lines, but it only worked with one domain for me,
# another one only got one cert, so I would recommend sepaate config files per domain.
domains =

# increase key size
rsa-key-size = 4096

# the current closed beta (as of 2015-Nov-07) is using this server
server =

# this address will receive renewal reminders, IIRC
email =

# turn off the ncurses UI, we want this to be run as a cronjob
text = True

# authenticate by placing a file in the webroot (under .well-known/acme-challenge/) and then letting
# LE fetch it
authenticator = webroot
webroot-path = /absolute/path/to/your/webroot/

To generate your first cert, open a shell and execute the letsencrypt-auto script:

# cd /root/letsencrypt
# ./letsencrypt-auto --config /etc/letsencrypt/configs/mydomain.conf certonly
Updating letsencrypt and virtual environment dependencies.......
Running with virtualenv: /root/.local/share/letsencrypt/bin/letsencrypt --config /etc/letsencrypt/configs/mydomain.conf certonly

 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/ Your cert will
   expire on 2016-02-05. To obtain a new version of the certificate in
   the future, simply run Let's Encrypt again.

Note the certonly command: we only want to issue certificates and don't want the client to fiddle with our nginx config.

nginx Integration

Simply update your nginx sites to use the new certificate and private key:

server {
  ssl_certificate /etc/letsencrypt/live/;
  ssl_certificate_key /etc/letsencrypt/live/;

That's it already.


I put a script in /etc/cron.monthly:

# create new certs
cd /root/letsencrypt
for conf in $(ls /etc/letsencrypt/configs/*.conf); do
  ./letsencrypt-auto --renew --config "$conf" certonly
# make sure nginx picks them up
service nginx restart

And now I get new certs on the first of every month. Done.

Adding new domains

Simply put new config files into /etc/letsencrypt/configs and run the command mentioned above once to get the initial cert.

Copy link

I agree with @jokesterfr, I just tested it, reload does not always pick up changed certificates, but sometimes does (A bit erratic behaviour)

Copy link

sudo nginx -s reload

Copy link

I followed this tutorial on Digital Ocean for setting up Let's Encrypt. It included a step for auto renewal.

Copy link

Just completed setting up certificates for a set of sites. Reload does not always work because of port binding to the IP address.

If you are using SNI to have multiple certificates on one IP address then you need a full restart. If you have just the one site or the primary site then the reload is fine.

Copy link

Looks like the latest version of 0.5.0 letsencrypt-auto (mine upgraded from 0.4.2) expects --renew-by-default instead of --renew in the cron script - e.g.

./letsencrypt-auto --renew-by-default --config "$conf" certonly

--renew no longer works and throws an error

Also, to test your cron monthly script you can use (as root):

run-parts -v /etc/cron.monthly

Copy link

You have to add following lines in your nginx config:

location ~ /.well-known {
                allow all;

otherwise it will response an unauthorized error.

Copy link

yves-s commented Apr 24, 2016

Even though I have:

location ~ /.well-known {
                allow all;

in my nginx config, I get an unauthorized error.

Type:   unauthorized
Detail: Invalid response from
challenge/a_GQh3k_GHmlzahNYP3enCWa0I6NN_yHZ0Ne7QHjZPM]: 404

How should it even be possible to reach the ".well-known" folder if the root is set to: "/var/www/"

My Nginx-configs are:

server {
        listen 80;

        root /var/www/;
        index index.html index.htm;


        location ~ /.well-known {
                allow all;

Copy link

xros commented Apr 25, 2016

@yves-s I had the same problem as you.

It turns out privilege issues.

What if the Nginx service was run by user "www-data", and this user "www-data" had no privilege to run the "bash" command.

If we run the cron script on user "root", it will return a 403 error (forbidden). Usually we set user to be ''www-data' in ubuntu destros.

Do you know how to solve it?

B.T.W. I solved it by a lame way that I temporarily set user root; in /etc/nginx/nginx.conf to let nginx run in root and it can visit folder .well-known/acme-challenge/ this folder was created by the python script letsencrypt-auto and deleted on the fly. It worked but it was lame.

The thing is that we need a user www-data to run the Python script letsencrypt-auto.

Copy link

b-jam commented Apr 27, 2016

@corburn how do you set the Common Name?
Mine is randomly choosing one from the list, I wish it just chose the first one or some way to choose the CN.

Copy link

Great gist. Have a look at my setup for ubuntu 16.04 with LE including nginx config:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment