DEV Community

kingyou
kingyou

Posted on

Automating HTTPS with Docker, Nginx & Certbot

Secure web traffic with HTTPS is no longer optional — it’s expected. This guide shows how to automate certificate issuance and renewal using Docker, Nginx and Certbot, packaged so deployment is repeatable and low-maintenance. Target audience: developers and ops engineers comfortable with Docker and basic Linux commands.


What you'll achieve

  • Deploy Nginx as a reverse proxy inside Docker.
  • Automatically obtain and renew Let's Encrypt TLS certificates using Certbot.
  • Keep configuration simple, reproducible, and safe for production.

Prerequisites

  • Docker and Docker Compose installed on a host with a public domain name pointing to its IP.
  • Port 80 and 443 reachable from the internet.
  • Basic familiarity with Nginx configuration and Docker Compose.

Project layout

Create a project directory; inside it, you’ll have:

  • docker-compose.yml
  • nginx/
    • conf.d/
    • default.conf (or site-specific conf)
  • certbot/
    • www/ (for HTTP challenge files)
  • data/ (certificate storage)

Step 1 — Write the Docker Compose file

Compose will run two services: nginx and certbot. Nginx serves traffic and proxies ACME HTTP-01 challenges to Certbot’s challenge directory.

Example docker-compose.yml:

version: "3.8"
services:
  nginx:
    image: nginx:stable-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./certbot/www:/var/www/certbot:ro
      - ./data:/etc/letsencrypt
    depends_on:
      - certbot
    restart: unless-stopped

  certbot:
    image: certbot/certbot
    volumes:
      - ./certbot/www:/var/www/certbot
      - ./data:/etc/letsencrypt
    entrypoint: /bin/sh -c "trap exit TERM; while :; do sleep 12h & wait $${!}; certbot renew --webroot -w /var/www/certbot --deploy-hook 'nginx -s reload' || true; done"
    restart: unless-stopped
Enter fullscreen mode Exit fullscreen mode

Notes:

  • Certificates live in ./data so they persist across container restarts.
  • Certbot is configured to periodically run renew and reload Nginx on success.

Step 2 — Configure Nginx to handle ACME challenges and proxy traffic

Create an Nginx server block that:

  • Serves /.well-known/acme-challenge from the certbot/www directory.
  • Proxies other requests to your upstream app.

Example nginx/conf.d/default.conf:

server {
    listen 80;
    server_name example.com www.example.com;

    location /.well-known/acme-challenge/ {
        alias /var/www/certbot/.well-known/acme-challenge/;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Basic SSL hardening (tune for your needs)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://your_upstream:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
Enter fullscreen mode Exit fullscreen mode

Replace example.com and your_upstream accordingly.


Step 3 — Obtain the initial certificate

Before HTTPS works, Certbot needs initial issuance. Run a one-off certbot command to request certificates using the webroot plugin.

Example command:

docker-compose run --rm certbot certonly \
  --webroot -w /var/www/certbot \
  -d example.com -d www.example.com \
  --email you@example.com \
  --agree-tos \
  --no-eff-email
Enter fullscreen mode Exit fullscreen mode

If successful, certificates are written under ./data/live/example.com. Then start or restart the stack:

docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

Certbot will handle renewals automatically via the background loop in docker-compose.


Step 4 — Validation & testing

  • Visit https://example.com and confirm the site loads with a valid certificate. Certificate should be issued by Let's Encrypt.
  • Test renewal simulation:
docker-compose run --rm certbot renew --dry-run --webroot -w /var/www/certbot
Enter fullscreen mode Exit fullscreen mode

Troubleshooting checklist

  • DNS: Ensure domain points to the host IP.
  • Ports: Confirm 80 and 443 are open and not blocked by firewall.
  • File permissions: Nginx must be able to read the certbot/www path.
  • Nginx config test: inside container run nginx -t to check syntax.

Security & operational tips

  • Store the data directory on encrypted disk if it contains sensitive keys.
  • Monitor certificate expiry with alerts (letsencrypt certs expire every 90 days).
  • Consider using a dedicated ACME container like dehydrated or an ACME client with webhook/notifications for more advanced flows.
  • For high-availability, move certificate issuance into a single central manager and distribute certs via secure sync.

Optional enhancements

  • Use Docker labels + Traefik to automate routing and certs (alternative to this stack).
  • Add HSTS, OCSP stapling and stronger cipher suites in Nginx for production hardening.
  • Automate backups of ./data with rotation.

Top comments (0)