Caddy
Introduction
The Caddy module renders declarative sites and endpoints for public or Tailscale ingress. Service-owning modules should declare their routes; host configurations generally enable Caddy, define users and roles, and set host-specific site behavior.
options.nix is the authoritative option reference.
Requirements
Caddy uses the Podman server for its container, storage datasets for runtime state, and agenix secrets for OAuth and basic-auth credentials. Public domains must resolve to the host before ACME certificate issuance can succeed.
Endpoints
Declare endpoints under a logical site:
caddy.sites.media.endpoints.example = {
type = "proxy";
path = "/example";
host = "example";
port = 8080;
role = "admin";
};Proxy endpoints require host and port.
Authentication defaults to OAuth; set auth = null only for
an intentionally public endpoint.
Useful endpoint settings include:
scheme = "https"for an HTTPS upstreamspoofBasic = truewhen the upstream should receive shared web basic-auth credentialsheaderUpfor additional upstream request headersstripPrefix = trueorhandlePath = truefor applications that need path-prefix handlingtype = "share"for a static share endpoint rather than a reverse proxy
The module validates common errors during evaluation, including incomplete proxies, missing or undeclared OAuth roles, duplicate endpoint paths within a site, and duplicate domain/listen-port pairs.
Sites And Domains
A site groups domains, endpoints, redirects, logging, and response behavior:
caddy.sites.example = {
domains = [
{
host = "example.bovbel.com";
tls = "public";
}
];
endpoints.app = {
type = "proxy";
path = "/";
host = "app";
port = 8080;
auth = null;
};
};Domain TLS can be public or tailscale;
listenPort can override the default listener. Prefer these
declarations over hand-written Caddyfile fragments.
Persistence
The module declares storage.datasets.app.children.caddy
and mounts it into the container. Preserve that dataset when rebuilding
or recovering the host; authentication state and certificates must not
be redirected to an ephemeral root path.
Route Audit
Caddy hosts receive a generated route audit at
/etc/caddy/routes.md. Use it to inspect the effective
domains, paths, authentication, and upstreams after deployment.
Troubleshooting
Inspect /etc/caddy/routes.md for the effective routes
and caddy-render.service for validation failures. The
rendered configuration and state live under
/storage/app/caddy/{Caddyfile,data,config}; check
caddy.service, apps-network.service, and the
container log when those paths are present but routing fails.
Authentication environment failures are reported by
podman-server-caddy-token-secret-env.service or
podman-server-caddy-basic-auth-env.service, while bans are
managed by fail2ban.service.
Options
caddy.enable
Enable containerized Caddy proxy and auth.
Type: boolean
Default:
falseDeclared by:
caddy.caddyfile
Rendered Caddyfile template derivation.
Type: package
Declared by:
caddy.cookieLifetime
OAuth cookie lifetime in seconds.
Type: positive integer, meaning >0
Default:
7884000Declared by:
caddy.email
ACME contact email for Caddy.
Type: string
Default:
"paul@bovbel.com"Declared by:
caddy.roles
Roles recognized by the authentication portal.
Type: list of string
Default:
[
"admin"
"user"
]Declared by:
caddy.routeSummary
Markdown summary of configured Caddy sites and endpoints.
Type: package (read only)
Declared by:
caddy.share.enable
Enable share endpoint.
Type: boolean
Default:
trueDeclared by:
caddy.sites
Caddy sites keyed by logical service name.
Type: attribute set of (submodule)
Default:
{ }Example:
{
media = {
domains = [
{
host = "media.example.com";
}
];
endpoints = {
app = {
host = "media";
path = "/";
port = 8080;
role = "admin";
type = "proxy";
};
};
};
}Declared by:
caddy.sites.<name>.domains
Domains that expose this site.
Type: list of (submodule)
Default:
[ ]Declared by:
caddy.sites.<name>.domains.*.host
Hostname served by this domain.
Type: string
Declared by:
caddy.sites.<name>.domains.*.listenPort
Explicit port on which Caddy listens for this domain.
Type: null or 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
nullDeclared by:
caddy.sites.<name>.domains.*.tls
TLS certificate source for this domain.
Type: one of “public”, “tailscale”
Default:
"public"Declared by:
caddy.sites.<name>.endpoints
Site endpoints keyed by logical name.
Type: attribute set of (submodule)
Default:
{ }Declared by:
caddy.sites.<name>.endpoints.<name>.auth
Authentication method, or null to disable authentication.
Type: null or one of “oauth”, “basic”
Default:
"oauth"Declared by:
caddy.sites.<name>.endpoints.<name>.handlePath
Whether to use Caddy’s handle_path directive.
Type: boolean
Default:
falseDeclared by:
caddy.sites.<name>.endpoints.<name>.headerUp
Additional header_up directive arguments passed to Caddy.
Type: list of string
Default:
[ ]Declared by:
caddy.sites.<name>.endpoints.<name>.host
Upstream host for a proxy endpoint.
Type: null or string
Default:
nullDeclared by:
caddy.sites.<name>.endpoints.<name>.path
Request path matched by this endpoint.
Type: string
Declared by:
caddy.sites.<name>.endpoints.<name>.port
Upstream port for a proxy endpoint.
Type: null or 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
nullDeclared by:
caddy.sites.<name>.endpoints.<name>.role
Role required to access this endpoint.
Type: null or string
Default:
nullDeclared by:
caddy.sites.<name>.endpoints.<name>.scheme
Upstream protocol for a proxy endpoint.
Type: one of “http”, “https”
Default:
"http"Declared by:
caddy.sites.<name>.endpoints.<name>.spoofBasic
Whether to derive an upstream basic authorization header from the authenticated user.
Type: boolean
Default:
falseDeclared by:
caddy.sites.<name>.endpoints.<name>.stripPrefix
Whether to strip the matched path prefix before proxying.
Type: boolean
Default:
falseDeclared by:
caddy.sites.<name>.endpoints.<name>.type
Endpoint handler type.
Type: one of “proxy”, “share”
Declared by:
caddy.sites.<name>.log
Whether to enable Caddy access logging for this site.
Type: boolean
Default:
falseDeclared by:
caddy.sites.<name>.notFound
Whether unmatched requests receive a not-found response.
Type: boolean
Default:
trueDeclared by:
caddy.sites.<name>.redirect
Target to which the site root redirects.
Type: null or string
Default:
nullDeclared by:
caddy.sites.<name>.securityHeaders
Whether to add the standard security response headers.
Type: boolean
Default:
trueDeclared by:
caddy.tokenLifetime
OAuth token lifetime in seconds.
Type: positive integer, meaning >0
Default:
7884000Declared by:
caddy.users
Users authorized through the Caddy authentication portal.
Type: list of (submodule)
Default:
[ ]Declared by:
caddy.users.*.email
Email address identifying the user.
Type: string
Declared by:
caddy.users.*.roles
Roles granted to the user.
Type: list of string
Default:
[ ]Declared by: