agentleFS
ddnettersddnetters/homelab-agent-skills/caddy-reverse-proxy/SKILL.md

caddy-reverse-proxy

Skill5 starsChanged 8 months agoMITView on GitHub

Caddy reverse proxy configuration, Caddyfile syntax, automatic HTTPS, Docker integration, and common patterns

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use {env.VAR_NAME} in Caddyfile:

    line 261

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Caddy Reverse Proxy

Caddy is a modern web server with automatic HTTPS. This skill covers Caddyfile configuration, reverse proxy patterns, and Docker deployment.

Caddyfile basics

# Reverse proxy to a backend service
app.example.com {
    reverse_proxy localhost:8080
}

# Multiple domains, same backend
app1.example.com, app2.example.com {
    reverse_proxy backend:3000
}

# Wildcard with on-demand TLS
*.example.com {
    tls {
        on_demand
    }
    reverse_proxy localhost:8080
}

Reverse proxy patterns

Basic proxy

service.example.com {
    reverse_proxy service:8080
}

With path stripping

example.com {
    handle /api/* {
        reverse_proxy api-server:3000
    }
    handle {
        reverse_proxy frontend:80
    }
}

WebSocket support (automatic)

Caddy handles WebSocket upgrades automatically. No extra config needed.

Load balancing

app.example.com {
    reverse_proxy node1:8080 node2:8080 node3:8080 {
        lb_policy round_robin
        health_uri /health
        health_interval 10s
    }
}

Headers

service.example.com {
    reverse_proxy backend:8080 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
}

TLS / HTTPS

Automatic (Let's Encrypt)

Caddy gets certificates automatically. Just use a domain name:

app.example.com {
    reverse_proxy localhost:8080
}

Cloudflare DNS challenge

For wildcard certs or when port 80/443 isn't publicly reachable. Requires the caddy-cloudflare build.

*.example.com {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }
    @app1 host app1.example.com
    handle @app1 {
        reverse_proxy app1:8080
    }
}

Internal / LAN-only (no public TLS)

:80 {
    reverse_proxy localhost:8080
}

# Or with self-signed cert
service.lan {
    tls internal
    reverse_proxy localhost:8080
}

Restrict to LAN only

service.example.com {
    @denied not remote_ip 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
    respond @denied 403

    reverse_proxy backend:8080
}

Common patterns

Catch-all / landing page

*.example.com {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }

    # Named matchers for each service
    @grafana host grafana.example.com
    handle @grafana {
        reverse_proxy grafana:3000
    }

    @n8n host n8n.example.com
    handle @n8n {
        reverse_proxy n8n:5678
    }

    # Fallback for unmatched subdomains
    handle {
        respond "Nothing here" 404
    }
}

Basic auth

admin.example.com {
    basicauth {
        # Generate hash: caddy hash-password
        admin $2a$14$HASHED_PASSWORD_HERE
    }
    reverse_proxy backend:8080
}

File server

files.example.com {
    file_server browse {
        root /srv/files
    }
}

Redirect HTTP to HTTPS (automatic)

Caddy does this by default. To redirect a domain:

old.example.com {
    redir https://new.example.com{uri} permanent
}

Docker deployment

docker-compose.yml

services:
  caddy:
    image: caddy:2-alpine
    # Or for Cloudflare DNS: caddybuilds/caddy-cloudflare
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    environment:
      - CLOUDFLARE_API_TOKEN=your-token  # if using DNS challenge
    restart: unless-stopped

volumes:
  caddy_data:
  caddy_config:

Connecting to other Docker services

Services on the same Docker network are reachable by container name:

app.example.com {
    reverse_proxy container-name:8080
}

For services on different networks, use the host IP or join networks:

services:
  caddy:
    networks:
      - frontend
      - backend

Management commands

# Validate config
caddy validate --config /etc/caddy/Caddyfile

# Reload without downtime (inside container)
docker exec <caddy-container> caddy reload --config /etc/caddy/Caddyfile

# Adapt Caddyfile to JSON (debugging)
caddy adapt --config /etc/caddy/Caddyfile

# View loaded config
caddy config

# Hash a password for basicauth
caddy hash-password

Environment variables

Use {env.VAR_NAME} in Caddyfile:

service.example.com {
    reverse_proxy {env.BACKEND_HOST}:{env.BACKEND_PORT}
}

Logging

service.example.com {
    log {
        output file /var/log/caddy/access.log
        format json
        level INFO
    }
    reverse_proxy backend:8080
}

Troubleshooting

ProblemFix
Certificate not issuingCheck DNS points to Caddy, ports 80/443 open, or use DNS challenge
502 Bad GatewayBackend not running or wrong hostname/port. Check docker network ls
Config not reloadingUse caddy reload, not restart (avoids downtime). Check caddy validate first
Wildcard cert failingRequires DNS challenge plugin (e.g. caddy-cloudflare), not default image
WebSocket disconnectingUsually a proxy timeout. Add transport http { keepalive 30s }
LAN service reachable externallyAdd remote_ip matcher to restrict access

Caddy vs nginx quick reference

nginxCaddy
proxy_pass http://backend;reverse_proxy backend:8080
ssl_certificate /path/cert.pem;Automatic (just use domain name)
location /api { ... }handle /api/* { ... }
nginx -t && nginx -s reloadcaddy validate && caddy reload
Separate config for HTTPS redirectAutomatic

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.