Running NGINX with Docker with Let’s Encrypt certificates outside of the container

Published by Philipp Schuster on

In this post I want to explain briefly how I solved the problem to run NGINX with docker but keep the let’s encrypt certificates outside of the Docker image. Some approaches include the certificates into the container during the build, i.e. they are generated in the docker image. I wanted to decouple certificates and NGINX. Here’s my approach.

It assumes you have docker and certbot (Let’s Encrypt) already installed.

1. Get the certificates

Just generate your certificates like in every let’s encrypt tutorial. $ sudo certbot certonly --standalone --preferred-challenges http -d mydomain.tld -d www.mydomain.tld

2. Let NGINX in Docker container access the certificates

The nginx.conf inside the NGINX container expects the certificates as usual in /etc/letsencrypt/live/mmydomain.tld/ (path inside container, not inside host system!). In docker-compose.yml we make sure to have the following volumes configured:

      # for error.log (choose any volume path you want)
      - .my_docker_compose_volume/nginx:/var/log/nginx
      # nginx.conf expects ssl files here
      - /etc/letsencrypt/live/:/etc/letsencrypt/live
      - /etc/letsencrypt/archive:/etc/letsencrypt/archive
      # DH params (needs to be generated first, see e.g.: `openssl dhparam -out /etc/ssl/dhparam.pem 2048`
      - /etc/ssl/:/etc/ssl

That’s it.

3. Example configuration files


version: '3.7'

  # main nginx : reverse proxy for ssl
    restart: always
    container_name: nginx
    image: phip1611/nginx:latest
      - 80:80
      - 443:443
      - .docker_compose_volume/nginx:/var/log/nginx
      # Volumes we need for TLS
      - /etc/ssl:/etc/ssl # dhparams file
      - /etc/letsencrypt/live:/etc/letsencrypt/live # current certificate
      - /etc/letsencrypt/archive:/etc/letsencrypt/archive # all certificates (symlinks from /live points here)

nginx.conf (only SSL part)

worker_processes auto;

# to get access to additional modules from "nginx-extra" package
include /etc/nginx/modules-enabled/*.conf;

events {
  worker_connections 1024;

http {
  gzip            on;
  gzip_min_length 1000;
  gzip_proxied    expired no-cache no-store private auth;
  gzip_types      text/plain text/css text/xml
                  application/javascript application/json application/xml application/rss+xml image/svg+xml;

  # neccessary for many server{}-blocks
  server_names_hash_bucket_size 64;

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  more_clear_headers 'server';

  # Common TLS Config
  ssl_certificate     /etc/letsencrypt/live/mydomain.tld/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/mydomain.tld/privkey.pem;
  ssl_dhparam         /etc/ssl/dhparam.pem;
  ssl_protocols       TLSv1.2 TLSv1.3;
  ssl_session_cache   shared:SSL:10m;
  ssl_session_timeout 10m;
  ssl_ciphers         "EECDH-AESGCM:EDH+ESGCM:AES256+EECDH:AES256+EDH";
  ssl_prefer_server_ciphers on;
  add_header          Strict-Transport-Security "max-age=31557600; includeSubdomains" always;

  server {
    listen 80 default_server;
    listen [::]:80 default_server;
    return 301 https://$host$request_uri;

  server {
      server_name mydomain.tld;
      listen 443 ssl http2;
      listen [::]:443 ssl http2;
      location / {
        proxy_pass http://some-service-running-inside-docker:8080;
        # container needs to know it doesn't run on "http://some-service-running-inside-docker" 
        proxy_set_header 'X-Forwarded-Host' mydomain.tld;
        proxy_set_header 'X-Forwarded-Proto' https;
        proxy_set_header 'X-Forwarded-For' $remote_addr;
        proxy_set_header 'X-Forwarded-IP' $remote_addr;

Dockerfile (to build nginx)

FROM nginx:1.17.5
# to get nginx_more_headers
RUN apt-get update && apt-get install -y nginx-extras
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx
VOLUME /var/log/nginx

Any more questions? Feel free to ask!

Philipp Schuster

Hi, I'm Philipp and interested in Computer Science. I especially like low level development, making ugly things nice, and de-mystify "low level magic".


Leave a Reply

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