Running multiple Docker containers often leads to issues like irregular port numbers in URLs, lack of HTTPS, and manual service tracking. This becomes unmanageable quickly. Ask anyone who’s juggled five ports at 2 a.m., it’s not fun.

A single reverse proxy container addresses these issues. It handles TLS termination, routes traffic by hostname, and shields backend containers from direct public exposure. This article provides Docker Compose files for Nginx, Traefik, and Caddy with working HTTPS and container routes.


1. Pick the Reverse Proxy That Fits Your Setup

Choose based on your needs, not popularity. As of 2026, all three are solid choices.

Quick Decision Table

Tool Best for HTTPS / Let’s Encrypt effort Docker auto-discovery Ongoing config effort Common consideration
Caddy Simple setups, homelab, quick HTTPS Automatic No (static Caddyfile by default) Low Wildcard certificates need custom build or DNS plugin
Traefik Dynamic microservices, changing containers Low (label-based) Yes (native) Low after setup Docker socket exposure; avoid --api.insecure=true in production
Nginx High-traffic applications, maximum control Manual (Certbot) No High Manual certificate renewal, static configuration

Reverse Proxy Quick Picks infographic comparing Caddy (Easy HTTPS), Traefik (Auto Routing), Nginx (Max Control). Three simple columns, minimal design, no icons, charts, or numbers.

Choose Based on Your Situation

  1. Choose Caddy if straightforward HTTPS with minimal configuration is key.
  2. Choose Traefik if you have many Docker services that change frequently and prefer label-based routing.
  3. Choose Nginx if you need extensive control, high performance, and are comfortable with manual configuration and Certbot.

Real-World Tradeoffs

  • Performance: Nginx performs well at around 25,000+ requests per second with an average latency of about 4ms. Traefik handles about 18,000–20,000 requests per second with an average latency of about 5ms. Caddy is reliable but has slightly higher latency (around 6–7ms average), which is acceptable for most tasks.
  • Static vs dynamic config: Nginx uses static configuration, requiring file edits and reloads. Traefik uses dynamic configuration; add labels to a container, and routes update instantly without restarting.
  • Caddy automatically handles HTTPS for public domains upon the first request, eliminating the need for Certbot or cron jobs.
  • Traefik requires mounting the Docker socket, which needs careful security management (see DevOps best practices).
  • Nginx offers the most flexibility for custom headers, upstreams, and Web Application Firewall (WAF) integration.

2. Target Setup Used Throughout

Example Architecture

One reverse proxy container routes:

  • app1.example.comwhoami container
  • app2.example.comwhoami2 container

Placeholders Used in All Examples

Setting Value
Domain example.com
Subdomains app1.example.com, app2.example.com
ACME email you@example.com
Shared Docker network proxy
Compose project folder reverse-proxy/

3. Prerequisites

  1. Docker and Docker Compose installed on the server.
  2. DNS A/AAAA records for app1.example.com and app2.example.com pointing to your server IP.
  3. Ports 80 and 443 open on your firewall and/or router.
  4. Two backend containers running HTTP inside Docker (the whoami image works well for testing).

4. Create the Shared Docker Network

This step applies regardless of the chosen proxy.

  1. Run docker network create proxy.
  2. In every Compose file, attach both the proxy service and all application services to this network.
  3. Use Docker service names for upstream addresses (e.g., http://whoami:80), not IP addresses or localhost.

Common mistake: localhost inside a container refers to that container, not the host machine or another container. Always use service names. We’ve all done it at least once.


5. Option A , Caddy as a Docker Reverse Proxy (Fastest HTTPS)

Caddy manages certificate issuance and renewal automatically. No additional tools are required.

Create the Folders and Files

mkdir -p reverse-proxy/caddy
touch reverse-proxy/caddy/Caddyfile

Caddy Docker Compose

version: "3.8"

networks:
  proxy:
    external: true

volumes:
  caddy_data:
  caddy_config:

services:
  caddy:
    image: caddy:latest
    container_name: caddy
    restart: unless-stopped