Skip to content

Nginx in Docker with Local Let's Encrypt Certs

Docker Project folder

├── docker
│   ├── nginx
│   │   ├── docker-compose.yml
│   │   ├── cloudflare.ini
│   │   ├── nginx.conf
│   └── └── (any additional confs for server blocks)
└────────────
docker-compose.yml
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    network_mode: "host"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./websocket.conf:/etc/nginx/conf.d/websocket.conf
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
#    ports:  # Expose ports 80 and 443 if not running in host mode
#      - "80:80"
#      - "443:443"
    depends_on:
      - certbot

  certbot:
    image: certbot/dns-cloudflare  # Certbot with Cloudflare DNS plugin
    container_name: certbot
    network_mode: "host"
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
      - ./cloudflare.ini:/etc/letsencrypt/cloudflare.ini  # Cloudflare credentials
    entrypoint: > # Right off the bat we'll request certs of our domains
      /bin/sh -c "
      certbot certonly --non-interactive --quiet --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
      --email ${EMAIL} --agree-tos --no-eff-email --expand \
      --domains '*.local.mydomain.com' --domains '*.tail.mydomain.com';
# Here we'll sleep 24 hours and call the renew function
      while :; do  
        sleep 24h;
        certbot renew --non-interactive --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini;
      done"
cloudflare.ini
dns_cloudflare_api_token=<cloudflare api token with dns edit privilege>
nginx.conf
# Set the user that runs the Nginx worker processes.
user nginx;

# Automatically determine the number of worker processes based on available CPU cores.
worker_processes auto;

# Define the file where the master process ID is stored.
pid /run/nginx.pid;

# Include any additional module configurations located in /etc/nginx/modules-enabled/.
include /etc/nginx/modules-enabled/*.conf;

events {
    # Maximum number of simultaneous connections per worker process.
    worker_connections 1024;
}

http {
    # Hide Nginx version information in responses for security.
    server_tokens off;

    # SSL settings: Allow only secure TLS versions.
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    # Logging: Store access and error logs in default locations.
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # Define a global proxy settings block to avoid duplication in individual servers.
    # This helps ensure consistency across all proxy hosts.
    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # Prevent clickjacking by denying the ability to embed this site in iframes.
    add_header X-Frame-Options DENY;

    # Prevent MIME-sniffing to avoid security risks.
    add_header X-Content-Type-Options nosniff;

    # set timeout
    proxy_read_timeout 600s;
    proxy_send_timeout 600s;
    send_timeout       600s;

    # ------------- PROXY HOSTS -----------------------------

    # Redirect HTTP to HTTPS hosts for ALL *.local.thomasjwilde.com & *.tail.thomasjwilde.com 
    server {
        listen 80;
        listen [::]:80;
        server_name *.local.thomasjwilde.com *.tail.thomasjwilde.com;
        return 301 https://$host$request_uri;
    }

    # immich.local
    server {
        listen 443 ssl;
        listen [::]:443 ssl;
        server_name immich.local.thomasjwilde.com;

        # Let's Encrypt Certs generated by certbot container
        ssl_certificate        /etc/letsencrypt/live/local.thomasjwilde.com/fullchain.pem;
        ssl_certificate_key    /etc/letsencrypt/live/local.thomasjwilde.com/privkey.pem;

        # Enable HTTP Strict Transport Security (HSTS) for one year, including subdomains.
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

        # enable websockets: http://nginx.org/en/docs/http/websocket.html
        include /etc/nginx/conf.d/websocket.conf;

        # Proxy settings for different subdomains
        location / {
            proxy_pass http://127.0.0.1:2283;
        }
    }

    # Vaultwarden tail
    server {
        listen 443 ssl;
        listen [::]:443 ssl;
        server_name vault.tail.thomasjwilde.com;

        # Let's Encrypt Certs generated by certbot container
        # Note that the `local.thomasjwilde.com` cert included the *.tail.thomasjwilde.com domain
        ssl_certificate        /etc/letsencrypt/live/local.thomasjwilde.com/fullchain.pem;
        ssl_certificate_key    /etc/letsencrypt/live/local.thomasjwilde.com/privkey.pem;

        # Enable HTTP Strict Transport Security (HSTS) for one year, including subdomains.
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

        # Proxy settings for different subdomains
        location / {
            proxy_pass http://127.0.0.1:1776;
        }
    }

}

So I did try adding a task to the nginx container to perform a hot reload every 48h or so, however, wheven I tried this using entrypoint or command, this prevented docker logs from occuring. However, I can just create a Timer on the host to run the reload via a command,

/usr/bin/docker exec -it nginx nginx -s reload

To tail the logs:

tail -10 /home/thomas/docker/nginx/data/nginx/logs/access.log

Comments