Skip to content

Instantly share code, notes, and snippets.

Last active Aug 3, 2021
What would you like to do?
Replacing legacy certbot-auto with docker

Replacing existing certbot-auto installation on Ubuntu 14 with Docker

The problem: Legacy certbot-auto is no longer supported, and ACMEv1 challenge has been deprecated and removed. On newer systems, you can simply upgrade certbot using apt-get install --upgrade-only certbot and you'll be good to go.

However, on legacy servers (Ubuntu 14), it isn't possible to upgrade. Installing manually isn't good either because python3 uses python3.4 which has a broken pip installation (ugh).

Docker to the rescue!

First, confirm what your existing letsencrypt cronjob is (if applicable) with sudo crontab -l. In my case I see that there is a pre-hook and post-hook for starting and stopping the local nginx (which is managed by gitlab):

0 3 * * 1 /home/user/certbot/certbot-auto renew --no-self-upgrade --pre-hook "gitlab-ctl stop nginx" --post-hook "gitlab-ctl start nginx" --quiet

To stop nginx, I can do that prior to running the docker command (or pass along the binaries into docker so docker can access them). To utilize stand-alone mode (which is the default) we'll also have to forward port 80.


Install docker. That's about it actually. sudo apt-get install and confirm it works:

docker --version
Docker version 1.6.2, build 7c8fca2

service docker status
docker start/running, process 6044

In the event that you try running it, get an error about not being able to bind to port 80, the image may not successfully shut down. In this case, running the same command again may give you a docker error that the image is already created.

The easiest thing is to just confirm the process is still running, delete the image, and run it again. Alternatively, try running with docker start instead of docker run (which creates AND starts an image).

sudo docker ps -a
CONTAINER ID        IMAGE                    COMMAND                CREATED             STATUS              PORTS               NAMES
49ca53fe8746        certbot/certbot:latest   "certbot renew --sta   3 minutes ago                                               certbot

sudo docker rm certbot

The instructions from certbot give us a good starting point:

Testing with Dry-Run

Using --dry-run is really good because it'll let you experiment without writing files. I found that first, nginx was still running (so I had to stop it to free up port 80 sudo gitlab-ctl stop nginx). Then, port 80 wasn't accessible so I had to forward local port 80 to the docker instance.

sudo docker run -it --rm --name certbot \
-v "/etc/letsencrypt:/etc/letsencrypt" \
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
-p 80:80 \
certbot/certbot renew --standalone --dry-run

With any luck you will see that this works just fine. There are some complaints about gitlab-ctl stop nginx not found, but they are just warnings (we are doing that part manually). Then run it again without --dry-run.

After it is all said and done, turn nginx back on (sudo gitlab-ctl start nginx).

Thus, the whole script should now be:

gitlab-ctl stop nginx
docker run -it --rm --name certbot \
-v "/etc/letsencrypt:/etc/letsencrypt" \
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
-p 80:80 \
certbot/certbot renew --standalone
gitlab-ctl start nginx

which can be saved in a file and ran by cron.

crontab -l
0 3 * * 1 /home/user/

Setting up a fresh certificate

Setting up a fresh certificate is similar. Be sure to stop nginx first, though!

sudo /etc/init.d/nginx stop
sudo docker run -it --rm --name certbot \
-v "/etc/letsencrypt:/etc/letsencrypt" \
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
-p 80:80 \
certbot/certbot --standalone certonly

(follow the directions, then confirm cert is present in /etc/letsencrypt/live/)

Update nginx config to use the new certificate:

upstream unicorn_website_production {
  server unix:/tmp/unicorn.website_production.sock fail_timeout=0;

# https redirect example
server {
  listen 443;
  add_header  Strict-Transport-Security "max-age=0;";
  rewrite ^(.*)$1 permanent;

server {
  listen 443;

  #add_header Strict-Transport-Security "max-age=0;";
  ssl on;
      ssl_certificate /etc/letsencrypt/live/; # managed by Certbot
      ssl_certificate_key /etc/letsencrypt/live/; # managed by Certbot
  root /home/user/website/production/current/public;

  add_header Strict-Transport-Security "max-age=31536000";

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;

  try_files $uri @unicorn;
  location @unicorn {
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn_website_production;

  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  keepalive_timeout 10;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment