This repository's only job is to produce a custom Caddy binary (Caddy + a fixed set of plugins) and publish it as a GitHub Release that a separate (private Ansible) repo consumes. It contains no servers and no secrets, and is public so the release artifact can be fetched without auth.
Caddy's download server (caddy add-package) refuses one of the modules the fleet
needs — github.com/darkweak/storages/redis/caddy returns
HTTP 400 "not a registered Caddy module package path." It is only buildable with
xcaddy. So we build the binary ourselves —
pinned to exact versions and checksummed — instead of pulling it from an upstream
server we don't control.
-
Base Caddy:
v2.11.3(matches the fleet). -
Platform:
linux/amd64only. -
Static:
CGO_ENABLED=0— runs across Debian versions without glibc surprises. -
Modules compiled in (all version-pinned in
versions.env):Source package Version Registers module ID(s) github.com/caddyserver/cache-handlerv0.16.0cache,http.handlers.cachegithub.com/darkweak/storages/redis/caddyv0.0.19storages.cache.redisgithub.com/pberkel/caddy-storage-redisv1.8.0caddy.storage.redisgithub.com/caddy-dns/desecv1.1.0dns.providers.desecgithub.com/corazawaf/coraza-caddy/v2v2.5.0http.handlers.waf(directivecoraza_waf)caddy-dns/cloudflareis intentionally not included (legacy, removed from the fleet).The Coraza WAF (
http.handlers.waf) is compiled in but inert — it embeds the OWASP Core Rule Set v4 and does nothing until a downstream Caddyfile turns it on. See the note below.
These are read verbatim from ./caddy-linux-amd64 list-modules and are the strings the
downstream Ansible role asserts on — do not guess them:
- HTTP cache handler:
http.handlers.cache(v0.16.0) - Souin Redis cache storage:
storages.cache.redis(v0.0.19) - Caddy global Redis storage:
caddy.storage.redis(v1.8.0) - Coraza WAF handler:
http.handlers.waf(v2.5.0, Caddyfile directivecoraza_waf)
⚠️ Two distinct Redis modules are compiled in — don't confuse them. They register different IDs in different namespaces and do different jobs, and both can coexist:
storages.cache.redis(darkweak/storages/redis/caddy) is a storage backend for the Souin HTTP cache — it lives in Souin'sstorages.cache.*namespace, not Caddy's globalcaddy.storage.*.caddy.storage.redis(pberkel/caddy-storage-redis) is a backend for Caddy's global storage (TLS certs/keys and other Caddy state) — it lives incaddy.storage.*.Assert on the exact ID for whichever role you mean:
storages.cache.redisfor the cache backend,caddy.storage.redisfor global storage.
ℹ️
http.handlers.wafis compiled in but inert. It does nothing at runtime until a downstream Caddyfile uses thecoraza_wafdirective (which also needs the global{ order coraza_waf first }option), loads rules (e.g. the embedded OWASP CRS v4 viaload_owasp_crs), and runs withSecRuleEngine On. Alist-modulespresence check verifies availability, not active protection — do not read "module present" as "the fleet is WAF-protected." All WAF configuration, rule tuning, and false-positive handling live in the downstream (Ansible) repo, not here. The WAF also buffers request/response bodies and does not pass through WebSockets or SSE, so downstream configs must matcher- exclude streaming/upload/WebSocket routes — plan aDetectionOnly→ tune →Onrollout.
./caddy-linux-amd64 version reports v2.11.3.
Tags drive releases. The convention is:
v2.11.3-a5t.2
└──┬──┘ └─┬─┘
base build suffix — bump the .N on every rebuild of the same base
- Rebuild (bump a module/Go/xcaddy version): edit
versions.env, commit, then bump the-a5t.Nsuffix:git tag v2.11.3-a5t.3 && git push origin v2.11.3-a5t.3. - Caddy upgrade: bump
CADDY_VERSIONinversions.envand tag the new base, e.g.v2.11.4-a5t.1.
Pushing a v* tag triggers .github/workflows/build.yml,
which builds, verifies the required modules are present (the build never ships if a
module is missing), and publishes a release with two assets:
caddy-linux-amd64— the binary.SHA256SUMS—sha256sum -c-compatible, containing the line<sha256> caddy-linux-amd64.
workflow_dispatch (Actions → build → Run workflow) builds and verifies without
releasing — useful to test a versions.env bump on a branch before tagging.
Per-tag, GitHub exposes stable asset URLs (substitute the release <TAG>):
https://github.com/x6c-co/caddy/releases/download/<TAG>/caddy-linux-amd64
https://github.com/x6c-co/caddy/releases/download/<TAG>/SHA256SUMS
For example, the current release v2.11.3-a5t.4:
https://github.com/x6c-co/caddy/releases/download/v2.11.3-a5t.4/caddy-linux-amd64
https://github.com/x6c-co/caddy/releases/download/v2.11.3-a5t.4/SHA256SUMS
The SHA256 is in SHA256SUMS and in the release notes (also given pre-formatted as
sha256:<hash> for Ansible).
Reproduces the exact CI build (same xcaddy invocation, same versions):
# Prereqs: Go matching GO_VERSION in versions.env, and the pinned xcaddy:
go install github.com/caddyserver/xcaddy/cmd/xcaddy@v0.4.5
./build.sh # -> ./caddy-linux-amd64
./caddy-linux-amd64 list-modules | grep -E 'http.handlers.cache|storages.cache.redis|caddy.storage.redis|http.handlers.waf'versions.env pins GO_VERSION=1.26.2, not 1.24.x. The Redis module
(darkweak/storages/redis/caddy@v0.0.19) declares go 1.26.1 in its go.mod, which
dominates Caddy 2.11.3's own go 1.25.0 requirement — a 1.24/1.25 toolchain fails to
build it. build.sh sets GOTOOLCHAIN=local so a future dependency that demands a newer
Go fails loudly here instead of silently downloading a different toolchain (which would
defeat the pinning). If a bump raises the required Go, raise GO_VERSION to match.
A separate Ansible role consumes the release. It is not implemented here, but the artifact is designed to satisfy it, so keep these stable across releases:
get_urlthecaddy-linux-amd64asset withchecksum: "sha256:<from SHA256SUMS>".- Install to
/usr/bin/caddy,apt-mark hold caddy, restart. - Assert
caddy list-modulescontainshttp.handlers.cacheandstorages.cache.redis.
http.handlers.waf (Coraza) is also present from the release that introduces it onward, but it
is optional and inert: the role need not assert it and need not configure anything unless/until the fleet
actually enables the coraza_waf directive (see the WAF note above). Adding a module ID is
backward-compatible — it cannot break a contains assertion on the existing IDs.
Stability guarantees: the asset name (caddy-linux-amd64), the SHA256SUMS format
(<sha256> caddy-linux-amd64), and the registered module IDs above will not change
without a coordinated update to the consuming role.
| File | Purpose |
|---|---|
versions.env |
Single source of truth for every pinned version. |
build.sh |
Reproducible local build; CI runs the same script. |
.github/workflows/build.yml |
Build → verify → release on v* tags. |
LICENSE |
Apache-2.0 (matching Caddy). |
Apache-2.0, matching Caddy.