We’re going to leverage Docker, Cloudflare DNS, and Caddy to set up a reverse proxy for our home-lab, which is both easy to configure, and automatically handles SSL certs for us. Previously, I used Nginx Proxy Manager, but I’ve grown to not love this application, as its both difficult to have your configurations live in code, as well to have SSL certifications both be made, and renewed automatically. Additionally, I previously used Namecheap DNS, as my domains are registered there, but I have found their API to be less then desirable. This is due to requiring to have spent at least $50 on the platform to be enabled, and having a lower level of support in the open source community.

  1. Register a domain with a registrar, like Namecheap
  2. Create an account with Cloudflare. Be sure to activate your account by clicking the activation link in your email.
  3. Configure Namecheap to leverage Cloudflare as a nameserver 3
  4. After waiting up to 24 hours, use https://dash.cloudflare.com/ to have Cloudflare check if your Nameservers are setup correctly. You’ll receive an email when the domain is set correctly, stating “Status Active”.
  5. Now we’re going to write a Dockerfile for Caddy:
FROM caddy:builder AS builder

RUN caddy-builder \
    # Adds Cloudflare DNS Challenge SSL cert support
    # link to module: https://github.com/caddy-dns/cloudflare
    # Will have to use this link to modify our Dockerfile to work with
    # caddy-docker-proxy:
    # https://github.com/lucaslorentz/caddy-docker-proxy#custom-images
    # Adds ability to define subdomains in docker-compose.yaml file
    # link to module: https://github.com/lucaslorentz/caddy-docker-proxy
    # github.com/lucaslorentz/caddy-docker-proxy/v2

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy
  1. Now we’re going to write a Docker Compose service for Caddy:
version: "3.7"

      context: ./caddy
      dockerfile: Dockerfile
    container_name: caddy
    # From: https://hub.docker.com/_/caddy
    # Caddy ships with HTTP/3 support enabled by default. To improve the performance of this UDP based protocol,
    # the underlying quic-go library tries to increase the buffer sizes for its socket. The NET_ADMIN capability
    # allows it to override the low default limits of the operating system without having to change kernel
    # parameters via sysctl.
      - NET_ADMIN
      - 80:80
      - 443:443
      #- "443:443/udp"
      - ACME_AGREE=true
      - /var/run/docker.sock:/var/run/docker.sock
      - ${SERVER_CONFIG_BASE}/caddy:/data
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile
    restart: unless-stopped
  1. To configure Caddy, we can write a Caddyfile
*.acme.com {
  tls {
    dns cloudflare {$CLOUDFLARE_API_TOKEN}

  # random
  @test host test.acme.com
  handle @test {
    respond "This is a test, proving that Caddy is running :)"

  @modem host modem.acme.com
  handle @modem {

  @router host router.acme.com
  handle @router {

  handle {
    respond "The subdomain you attempted to navigate to is not configured."

# Refer to the Caddy docs for more information:
# https://caddyserver.com/docs/caddyfile
  1. At this point we’re able to run our caddy service by running $ docker compose up -d. This allows us to now access our entries in our Caddyfile in our web browser, by hitting the defined subdomains. For example, we can navigate to test.acme.com, which will show us a string in our web browser as defined in the response.


  1. Overall guide
  2. Docker image we’re relying on
  3. How to configure Namecheap to point to Cloud Flare DNS
  4. Interesting discussion between community and a Caddy developer