Caddy + Mercure + Brotli, with Souin and AnyCable variants. Rebuilt on upstream releases.
10K+
Caddy with Mercure and
Brotli compression, plus variants
with the Souin HTTP cache or
AnyCable. Each of these is
also available with layer4 (TCP/TLS
routing). A drop-in replacement for the official caddy image: same paths,
same default command.
New Caddy, Mercure and plugin releases are picked up automatically. Each one is built and smoke-tested before it is published.
Published to Docker Hub (bock/caddy8d) and GHCR (ghcr.io/bb/caddy8d) for
linux/amd64 and linux/arm64.
| Variant | Contents | Floating tags | Pinned tags |
|---|---|---|---|
base | Caddy, Mercure, cbrotli | latest, 2, 2.11, 2.11.7 | 2.11.7-20261004-0312 |
souin | base + Souin HTTP cache | souin, 2-souin, 2.11-souin, 2.11.7-souin | 2.11.7-souin-20261004-0312 |
anycable | base + AnyCable (WebSocket) | anycable, 2-anycable, 2.11-anycable, 2.11.7-anycable | 2.11.7-anycable-20261004-0312 |
l4 | base + layer4 | l4, 2-l4, 2.11-l4, 2.11.7-l4 | 2.11.7-l4-<build date> |
souin-l4 | souin + layer4 | souin-l4, 2-souin-l4, 2.11-souin-l4, 2.11.7-souin-l4 | 2.11.7-souin-l4-<build date> |
anycable-l4 | anycable + layer4 | anycable-l4, 2-anycable-l4, 2.11-anycable-l4, 2.11.7-anycable-l4 | 2.11.7-anycable-l4-<build date> |
The version in the tag is the Caddy version compiled in. The floating tags
follow rebuilds: new plugin releases and weekly Alpine updates. A dated tag
always points to one specific build, so use it to pin a server or roll back.
The plugin versions of each build are listed in the summary of the workflow
run that published it, and caddy list-modules --versions prints them too.
services:
caddy:
image: bock/caddy8d:2
environment:
MERCURE_PUBLISHER_JWT_KEY: ${MERCURE_PUBLISHER_JWT_KEY:?}
MERCURE_SUBSCRIBER_JWT_KEY: ${MERCURE_SUBSCRIBER_JWT_KEY:?}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./mercure:/mercure
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
See examples/ for a complete Caddyfile with two Mercure hubs.
The *-l4 variants add the layer4 app.
Its most useful mode next to websites is a listener wrapper: the HTTPS
server keeps port 443, and connections for selected TLS names are taken off
before HTTP sees them, terminated with the certificate Caddy already manages
for that name, and proxied as plain TCP. That puts, for example, a TURN
server (turns: over TLS) on 443 next to the websites, for clients on
networks that only allow port 443:
{
servers :443 {
listener_wrappers {
layer4 {
@turn tls sni turn.example.com
route @turn {
tls
proxy tcp/turn-backend:3478
}
}
tls
}
}
}
# Makes Caddy obtain and renew the certificate for the diverted name
turn.example.com {
respond 404
}
Every other connection falls through to the tls wrapper and is served as
usual; HTTP/3 (UDP 443) is not affected. The smoke test checks both paths on
one port. Upstream marks the layer4 app as experimental: its configuration
may change between releases.
Mercure 1.0 changed the protocol: tokens are OAuth access tokens, subscribers
use match= instead of topic=, and JWT keys move into issuer blocks
(upgrade guide).
A 0.x Caddyfile fails to start without changes. These images are built
with the same compatibility tags as the official Mercure builds, so existing
apps keep working once each mercure block gets two changes:
mercure {
name app # required when there is more than one hub
protocol_version_compatibility 8 # accept 0.x clients and tokens
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY}
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY}
transport bolt {
path /mercure/app-mercure.db
}
}
Compatibility mode relaxes token validation and accepts the token in the URL
query, so also redact authorization from access logs (see
examples/Caddyfile). Plan to migrate the apps' tokens
and subscribe URLs and then remove protocol_version_compatibility.
sse true, /events
answers 501: anycable-go checks for http.Flusher directly, and Caddy's
response writer doesn't satisfy that check. WebSockets (/cable, the
normal ActionCable transport) work.anycable variants stay on Caddy 2.11.7 (and Mercure 1.1.5, since
Mercure 1.1.6 requires Caddy 2.11.8). On Caddy 2.11.8 and 2.11.9 the
AnyCable WebSocket upgrade answers 500 when encode runs in front of it;
without encode it works. reverse_proxy WebSockets behind encode are
not affected, so the other variants use the latest Caddy. The next Caddy
release is tried again automatically.variants/souin.caddy base image can lag behind Caddy releases. Only the
binary is replaced, so trust caddy version, not the base image tag../build.sh # build and smoke-test all six variants
./build.sh souin # just one
tests/smoke.sh caddy8d:base base
Each variant is a small Go module in variants/<name>/ (main.go imports the
plugins, go.mod pins their versions). To add a plugin, add its import to
main.go and run go get <module>@latest && go mod tidy in that directory.
This repository is licensed under the AGPL-3.0. The images bundle
Caddy and caddy-l4 (Apache-2.0), Mercure (AGPL-3.0), and cbrotli, Souin and
caddy_anycable (MIT), all unmodified. This repository (the go.mod/go.sum files pin exact
versions) is the corresponding source.
Content type
Image
Digest
sha256:02707c4d2…
Size
44.3 MB
Last updated
3 days ago
docker pull bock/caddy8d