caddy-config uses Go's text/template engine to render site blocks. While standard reverse proxying and redirects work out-of-the-box, you can customize the entire site block for any instance.
When an instance has no template label, caddy-config applies the default site template:
{{ .Domain }} {
{{- if .Redirect }}
redir {{ .Redirect }} permanent
{{- else if .Upstreams }}
reverse_proxy {{ range $i, $u := .Upstreams }}{{ if $i }} {{ end }}{{ $u }}{{ end }}
{{- end }}
}
Inside your custom template, the following fields are available:
| Variable | Type | Description |
|---|---|---|
.Domain |
string |
The domain(s) defined on the instance (user.label.<prefix>.domain). |
.Upstreams |
[]string |
Sorted list of resolved upstream addresses (e.g. ["10.0.1.5:8080", "10.0.1.6:8080"]). |
.Redirect |
string |
The redirect URL if configured (user.label.<prefix>.redirect). |
.Template |
string |
The raw template name or inline template string. |
You can provide custom templates in two ways:
Specify the template directly in the template label:
services:
web:
image: docker.io/library/nginx:alpine
labels:
edge.domain: "spa.example.com"
edge.upstream: "8080"
edge.template: |
{{ .Domain }} {
encode gzip zstd
reverse_proxy {{ index .Upstreams 0 }}
}
--custom-templates-dir)For reusable site configurations, store templates in a directory mounted to caddy-config and pass --custom-templates-dir /etc/caddy/templates.
When an instance specifies edge.template: "spa_site", caddy-config searches the custom templates directory in the following order:
/etc/caddy/templates/spa_site/etc/caddy/templates/spa_site.tmpl/etc/caddy/templates/spa_site.caddyfile# /etc/caddy/templates/secure_proxy.caddyfile
{{ .Domain }} {
encode gzip zstd
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
reverse_proxy {{ range $i, $u := .Upstreams }}{{ if $i }} {{ end }}{{ $u }}{{ end }}
}
Usage in Compose:
services:
app:
image: my-app:latest
labels:
edge.domain: "secure.example.com"
edge.upstream: "3000"
edge.template: "secure_proxy"
Useful for single-page applications (React, Vue) or microservices serving static assets:
# /etc/caddy/templates/spa_app.caddyfile
{{ .Domain }} {
root * /var/www/dist
file_server
try_files {path} /index.html
handle /api/* {
reverse_proxy {{ index .Upstreams 0 }}
}
}
Connects directly to an upstream PHP container running php-fpm:
# /etc/caddy/templates/php_site.caddyfile
{{ .Domain }} {
root * /var/www/html
php_fastcgi {{ index .Upstreams 0 }}
file_server
}
Usage in Compose:
services:
wordpress:
image: docker.io/library/wordpress:fpm-alpine
labels:
edge.domain: "blog.example.com"
edge.upstream: "9000"
edge.template: "php_site"
Optimized for real-time applications requiring continuous flushes and keep-alives:
# /etc/caddy/templates/websocket_proxy.caddyfile
{{ .Domain }} {
reverse_proxy {{ range $i, $u := .Upstreams }}{{ if $i }} {{ end }}{{ $u }}{{ end }} {
flush_interval -1
}
}
Every custom template is rendered in-memory and validated using the Caddy binary inside the container (caddy validate) before deployment.
If a custom template contains an invalid directive, bad syntax, or broken references:
caddy validate fails inside the container.caddy-config logs the exact error returned by Caddy.