Caddy reverse proxy with automatic HTTPS illustration

Caddy Reverse Proxy

Caddy Reverse Proxy

Caddy is an open-source web server that doubles as a reverse proxy and requests its own HTTPS certificates automatically, with no separate resolver step to configure. Write a domain name and a reverse_proxy line into a short Caddyfile, start the process, and the site is live over real HTTPS within seconds.

License: Apache License 2.0, confirmed directly from the LICENSE file in the caddyserver/caddy GitHub repo. Free for any use, including commercial. Caddy has been in development since 2014, started by Matthew Holt, and is now a project of ZeroSSL, an HID Global company. The name is trademarked, and the maintainers ask that it be called “Caddy” or “the Caddy web server,” not “Caddy Server.”

Quick facts: official site caddyserver.com · source github.com/caddyserver/caddy (73,000+ stars) · Docker Official Image caddy on Docker Hub · latest stable v2.11.4 · multi-arch (amd64, arm64, armv6/v7, plus ppc64le, riscv64, s390x, and Windows variants) · admin API listens on 127.0.0.1:2019 by default · written in Go.

Why Caddy instead of Nginx Proxy Manager or Traefik?

All three reverse proxies covered on this site solve the same problem, routing a domain to a container with a valid certificate, but each asks something different of the person setting it up. Nginx Proxy Manager trades a config file for a dashboard form; Traefik trades the dashboard for Docker labels it discovers on its own. Caddy keeps a text file too, but drops the Docker socket and label syntax entirely: a Caddyfile lives on disk and takes effect on reload, no container relationship and no dashboard to lock down. The trade-off is that Caddy won’t notice a new container by itself, every site block still gets added by hand, just a shorter one than an Nginx block or a page of Traefik labels.

Setting up Caddy with Docker

This follows the pattern from Caddy’s own Docker Official Image and reverse proxy quick-start: a Caddyfile mounted from the host, plus two named volumes so certificates survive container restarts.

services:
  caddy:
    image: caddy:2.11-alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./conf:/etc/caddy
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:
  1. Install Docker if it isn’t already on the host, create a conf folder next to docker-compose.yml, and save a Caddyfile inside it, covered next.
  2. Save the file above and run docker compose up -d.
  3. Forward ports 80 and 443 on your router to this machine; the UDP 443 mapping is for HTTP/3, optional but worth keeping.
  4. Mount the conf folder, not a single Caddyfile; some editors change the file’s inode on save, which breaks Caddy’s reload.
docker compose up -d
docker compose logs -f caddy

Routing traffic with the Caddyfile

This replaces Nginx Proxy Manager’s Add Proxy Host form and Traefik’s container labels: the rule is a domain name followed by a reverse_proxy line.

jellyfin.yourdomain.com {
    reverse_proxy jellyfin:8096
}

Save that inside ./conf/Caddyfile and restart the container. Caddy requests a certificate for jellyfin.yourdomain.com as soon as it sees DNS pointed at it correctly, no separate command and no resolver block, the step Traefik needs its certificatesresolvers flags for.

Tip: Caddy validates a config change and reloads without dropping existing connections. Run docker exec -w /etc/caddy caddy caddy reload after editing the Caddyfile instead of restarting the container.

How Caddy’s automatic HTTPS actually works

Every other reverse proxy on this site needs a deliberate step for HTTPS: Nginx Proxy Manager’s SSL tab, Traefik’s certificate resolver flags. Caddy checks each domain name in the Caddyfile and, if it looks like a real public hostname, requests a certificate right away, falling back to a second issuer if the first is unreachable. Internal hostnames and bare IPs get certificates too, from a private, locally-managed authority, so LAN-only services end up behind real TLS instead of a self-signed warning.

Security notes: Caddy’s admin API binds to 127.0.0.1:2019 by default, unlike Traefik’s dashboard on port 8080, which starts open unless someone locks it down. Never rebind it to a public interface without authentication in front. The /data volume holds every certificate and private key Caddy issues; back it up, since Let’s Encrypt punishes repeated re-issuance with rate limits. Pin the image to a specific version instead of :latest before exposing this to the internet.

Caddy vs Traefik vs Nginx Proxy Manager

All three terminate HTTPS and forward traffic to the right container. What changes between them is how each one learns where traffic should go, and how much of that process it automates.

CaddyTraefikNginx Proxy Manager
ConfigurationCaddyfile, a few lines per siteDocker labels, auto-discoveredWeb dashboard, added by hand
Service discoveryNone, edited by handAutomatic via Docker, Kubernetes, SwarmNone, every host added manually
HTTPSAutomatic, on by default, no resolver configAutomatic via Let’s Encrypt resolverAutomatic via the dashboard’s SSL tab
Setup difficultySimple, text-basedSteepest of the threeEasiest, GUI-driven
Best forSmall setups, text config over UIDocker-heavy hosts with many containersA first reverse proxy, GUI preference
LicenseApache 2.0MITMIT
  • Automatic HTTPS the moment a domain resolves, no resolver flags or SSL tab required
  • Config lives in one short, readable Caddyfile instead of labels scattered across containers or a dashboard database
  • HTTP/3 and modern TLS defaults supported out of the box
  • Apache 2.0-licensed, no feature paywall
  • No web UI and no container auto-discovery; every site block is still added by hand
  • Fewer homelab tutorials and community guides exist for Caddy than for Traefik or NPM
  • Adding plugins or DNS providers means building a custom binary with xcaddy, not flipping a config toggle

Hardware: Caddy is a single Go binary with no external dependencies, not even libc in the Alpine image, light enough for a Raspberry Pi or any low-power always-on box; our homelab setup guide for beginners covers picking that first machine. The real requirements are ports 80 and 443 reachable from outside, and a persistent /data volume for certificates.

Reaching a self-hosted service like Jellyfin from outside the house is one of the most common reasons homelabbers reach for a reverse proxy in the first place. Our Jellyfin remote access guide walks through that setup end to end, and weighs it against Tailscale and WireGuard, which expose a full private network instead of routing one HTTPS subdomain at a time.

FAQ

Is Caddy free and open source?

Yes. It’s Apache 2.0-licensed, free for any use including commercial, and maintained as a ZeroSSL project rather than sold as a separate paid product.

Is Caddy better than Nginx or Traefik?

For sites with real domain names, Caddy usually needs the least setup: no dashboard to secure like Nginx Proxy Manager, no resolver flags or Docker socket like Traefik. Traefik wins once a host runs a dozen-plus auto-discovered containers; Nginx Proxy Manager wins for anyone who’d rather fill in a form.

Does Caddy need Docker to run?

No. Caddy is a single binary for Linux, macOS, Windows, and BSD with no container runtime needed; Docker is just the common way homelabbers run it.

Can Caddy issue certificates for internal-only hostnames?

Yes. For names or IPs that aren’t publicly resolvable, Caddy falls back to a locally-managed certificate authority instead of Let’s Encrypt, so LAN-only services still get real TLS.

Is Caddy’s admin API safe to expose to the internet?

Not without authentication in front of it. It binds to 127.0.0.1:2019 by default, which is already safer than an open port, and that default should stay untouched rather than get rebound to a public interface.

For a couple of sites with real domains, Caddy is usually the fastest way from a docker-compose file to a working HTTPS address without touching a certificate flag. Once a homelab needs a dashboard for less technical household members, or grows past a dozen auto-discovered containers, that’s the point where Nginx Proxy Manager or Traefik starts to make more sense instead.