Docker

Painless HTTPS for Docker with NGINX

I forget when I started using docker, maybe around 2015 or so. Ever since then I would start these grand projects, take them 50% of the way and get stuck. A few months or even years would pass and I’d get a little further and get stuck again, rinse and repeat.

Perhaps one of the biggest hurdles I could never overcome was automating certificate installs and renewals for docker containers.

Using the good ole’ certbot is the tried and true method, but here’s the issue…

How do you get those certs into the container? You can’t even generate the certs from the host machine if the web service is in a container!? You can’t install certbot inside containers because they don’t have cron or systemd.

As time passed I saw a few solutions to this including but not limited to Caddy and the official certbot docker image. However the one I’ve had the most success with is the jwilder/nginx-proxy docker image.

How does it work? Well it consists of 2 containers, one for the nginx-proxy and another to run the letsencrypt automation.

The setup is mostly generic and once it’s in place it requires very little maintenance or interaction. In fact once you have it setup and understand how to utilize it any web services you spin up will automatically link with it, get the certs and install them automatically in seconds without any interaction. Renewals work similarly and run automatically on their own. Truly jwilder is a giant among men!

I will post my docker-compose.yml configuration below for your benefit. It’s similar to the default config with only one modification that is optional.

A link to this in GitHub can be found here.

version: "3" 
services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - letsencrypt-certs:/etc/nginx/certs
      - letsencrypt-vhost-d:/etc/nginx/vhost.d
      - letsencrypt-html:/usr/share/nginx/html
      - ./nginx:/etc/nginx  #optional
  letsencrypt-proxy:
      image: jrcs/letsencrypt-nginx-proxy-companion
      container_name: letsencrypt-proxy
      restart: unless-stopped
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock:ro
        - letsencrypt-certs:/etc/nginx/certs
        - letsencrypt-vhost-d:/etc/nginx/vhost.d
        - letsencrypt-html:/usr/share/nginx/html
      environment:
        - DEFAULT_EMAIL=youremail@domain.com
        - NGINX_PROXY_CONTAINER=nginx-proxy
networks:
  default:
    external:
      name: nginx-proxy
 
volumes:
  letsencrypt-certs:
  letsencrypt-vhost-d:
  letsencrypt-html:

The only modification I’ve made is mounting the /etc/nginx configuration as a volume just so I can look at the configs and make modifications if necessary from the host system.

When setting up a docker instance I highly recommend running this as your first docker project because doing so will make everything else later so much easier. Once you start spinning up other docker projects on port 80 or port 443 it will become more difficult to get this going. So do this first!

When setting up other docker services later, if you want to use the nginx-proxy you need to

  1. Make sure the project is on the same network as the nginx-proxy
  2. Add the virtual host for the VIRTUAL_HOST environment variable and LETSENCRYPT_HOST environment variable
  3. Optionally you may need a VIRTUAL_PORT set if you are using something besides Port 80 or 443

Below is a snippet from a jellyfin project as an example of all 3:

  1 ---
  2 version: "2.1"
  3 services:
  4   jellyfin:
  5     image: lscr.io/linuxserver/jellyfin:latest
  6     container_name: jellyfin
  7     environment:
  8       - PUID=1000
  9       - PGID=1000
 10       - TZ=America/Chicago
 11       - JELLYFIN_PublishedServerUrl=media.zmail.tech #optional
 12       - VIRTUAL_HOST=${VIRTUAL_HOST} #added for nginx
 13       - LETSENCRYPT_HOST=${VIRTUAL_HOST} #added for LE automation
 14       - VIRTUAL_PORT=8096

Here is the .env file for those variables

LETSENCRYPT_EMAIL=your@email.com
VIRTUAL_HOST=your.website.com

I will post an article about this jellyfin setup later.

So that’s all! Hope this helps!

Leave a Reply

Your email address will not be published. Required fields are marked *