Lets Encrypt + Haproxy

I've used a few different approaches for renewing the Let's Encrypt certs for my domain over the years, but I recently found this great docker image that encapsulates everything into a single container.

Steps

  1. Create an haproxy.cfg file
  2. Run the docker container

Create an haproxy cfg file

Here's my Haproxy config file, slightly modified from the one provided in the repo since I'm serving my site on port 8080 locally. I've stored it in /etc/haproxy/haproxy.cfg

global
    maxconn 20480
    ############# IMPORTANT #################################
    ## DO NOT SET CHROOT OTHERWISE YOU HAVE TO CHANGE THE  ##
    ## acme-http01-webroot.lua file                        ##
    # chroot /jail                                         ##
    #########################################################
    lua-load /etc/haproxy/acme-http01-webroot.lua
    #
    # SSL options
    ssl-default-bind-ciphers AES256+EECDH:AES256+EDH:!aNULL;
    tune.ssl.default-dh-param 4096

    # workaround for bug #14 (Cert renewal blocks HAProxy indefinitely with Websocket connections)
    hard-stop-after 3s

# DNS runt-time resolution on backend hosts
resolvers docker
    nameserver dns "127.0.0.11:53"

defaults
    log global
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    option forwardfor
    option httplog

    # never fail on address resolution
    default-server init-addr last,libc,none

frontend http
    bind *:80
    mode http
    acl url_acme_http01 path_beg /.well-known/acme-challenge/
    http-request use-service lua.acme-http01 if METH_GET url_acme_http01
    redirect scheme https code 301 if !{ ssl_fc }

frontend https
    bind *:443 ssl crt /etc/haproxy/certs/ no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11
    http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;"
    default_backend www

backend www
    server ghost localhost:8080 check
    http-request add-header X-Forwarded-Proto https if { ssl_fc }

Run the docker container

Once you have the haproxy file set up, run the following command:

DOMAINS="[YOUR COMMA SEPARATED LIST OF DOMAINS]"
EMAIL="[YOUR EMAIL]"
docker run --name haproxy -d \
    --net="host" \
    -e CERTS=$DOMAINS \
    -e EMAIL=$EMAIL \
    -e STAGING=false \
    --restart=always \
    -v /home/ubuntu/haproxy:/etc/haproxy \
    -p 80:80 -p 443:443 \
    ghcr.io/tomdess/docker-haproxy-certbot:master

That's it! The container runs a cron job that checks your cert weekly and updates it if required