caddy-reverse-proxy/SKILL.md
Caddy reverse proxy configuration, Caddyfile syntax, automatic HTTPS, Docker integration, and common patterns
npx skillsauth add ddnetters/homelab-agent-skills caddy-reverse-proxyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Caddy is a modern web server with automatic HTTPS. This skill covers Caddyfile configuration, reverse proxy patterns, and Docker deployment.
# 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
}
service.example.com {
reverse_proxy service:8080
}
example.com {
handle /api/* {
reverse_proxy api-server:3000
}
handle {
reverse_proxy frontend:80
}
}
Caddy handles WebSocket upgrades automatically. No extra config needed.
app.example.com {
reverse_proxy node1:8080 node2:8080 node3:8080 {
lb_policy round_robin
health_uri /health
health_interval 10s
}
}
service.example.com {
reverse_proxy backend:8080 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
Caddy gets certificates automatically. Just use a domain name:
app.example.com {
reverse_proxy localhost:8080
}
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
}
}
:80 {
reverse_proxy localhost:8080
}
# Or with self-signed cert
service.lan {
tls internal
reverse_proxy localhost:8080
}
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
}
*.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
}
}
admin.example.com {
basicauth {
# Generate hash: caddy hash-password
admin $2a$14$HASHED_PASSWORD_HERE
}
reverse_proxy backend:8080
}
files.example.com {
file_server browse {
root /srv/files
}
}
Caddy does this by default. To redirect a domain:
old.example.com {
redir https://new.example.com{uri} permanent
}
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:
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
# 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
Use {env.VAR_NAME} in Caddyfile:
service.example.com {
reverse_proxy {env.BACKEND_HOST}:{env.BACKEND_PORT}
}
service.example.com {
log {
output file /var/log/caddy/access.log
format json
level INFO
}
reverse_proxy backend:8080
}
| Problem | Fix |
|---------|-----|
| Certificate not issuing | Check DNS points to Caddy, ports 80/443 open, or use DNS challenge |
| 502 Bad Gateway | Backend not running or wrong hostname/port. Check docker network ls |
| Config not reloading | Use caddy reload, not restart (avoids downtime). Check caddy validate first |
| Wildcard cert failing | Requires DNS challenge plugin (e.g. caddy-cloudflare), not default image |
| WebSocket disconnecting | Usually a proxy timeout. Add transport http { keepalive 30s } |
| LAN service reachable externally | Add remote_ip matcher to restrict access |
| nginx | Caddy |
|-------|-------|
| 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 reload | caddy validate && caddy reload |
| Separate config for HTTPS redirect | Automatic |
development
Use when delegating a single coding task to `codex exec` ("hand off to codex", "run codex on this", "dispatch codex on this ticket", any one-shot invocation). Covers flags, sandbox traps, monitoring, and recovery. Not for multi-issue parallel batches — use codex-issue-waves for those.
development
Use when the user says "have codex fix this" / "have codex implement this" / "let codex handle this" / "give this to codex" / "delegate this to codex" for a single task with context already in scope (a Jira ticket, GitHub issue, file diff, bug, or described change). Plans the work, splits it into reviewable waves, dispatches codex per wave with review and correction between waves before opening a PR. Not for multi-issue parallel batches (use codex-issue-waves) or one-shot codex runs without planning (use invoking-codex-exec).
development
Run a batch of GitHub issues through codex exec in isolated git worktrees as parallel autonomous PRs, then manage the review and correction waves until merge. Use when the user gives a list of issue numbers (≥ 2) and asks to "spawn codex" / "dispatch codex" / "have codex work on" / "manage the PRs" / "process feedback" / "get them merged" for those issues, or when the user asks for multi-issue parallel delegation to codex. Not for single-issue wave-driven delegation (use codex-task-waves), single-issue one-shot dispatch (use invoking-codex-exec), or implementation without delegation (use /pr or direct implementation).
development
Slite knowledge base API — ask questions, search notes, retrieve content, manage users and groups, and audit knowledge health via the REST API