The container installed Debian trixie's Caddy, 2.6.2, from 2022, while the edge runs Alpine's 2.11. A Go stage now builds Caddy v2.11.7, the latest patch of the edge's line (the edge's Alpine 3.24 has 2.11.4), the way tailcat, rclone and litestream are built: CGO_ENABLED=0, go install at a pinned version, the source checked by the Go checksum database. It is built as Caddy builds its releases (-trimpath, -s -w), which keeps the binary at 53M instead of 76M, and with CustomVersion, since a build of Caddy's own module reports "unknown" otherwise. Without Debian's package, the Dockerfile makes what it made: the caddy system user (home /var/lib/caddy, no login shell), added to the git group as before, and /etc/caddy, where start.sh writes the Caddyfile. media-types stays, for /etc/mime.types: Go only knows a few content types itself. libnss3-tools, which the package pulled in for caddy trust, goes. start.sh, fcgiwrap and the generated Caddyfile are unchanged; caddy adapt takes the forge's Caddyfile without a warning. The image is about the same size: 447M of content, from 446M. README.md says which tools the Dockerfile builds and which Caddy, and test/container.sh no longer calls the container's Caddy Debian's.
- commit
- 3006d6d778786a3a988980e83b98ac3c84f9b0d5
- parent
- 3282a9991a0ca1d320ca293d12609e046663306d
- author
- cat <admin@dev.site>
- ci
- ✓ passed in 133s, signed by cat
- kind
- build risk score 1.6 of 3, Jev's estimate
- review
- #7 Run a pinned Caddy 2.11 in the container
ci attestation
forge-attestation 1 type ci commit 3006d6d778786a3a988980e83b98ac3c84f9b0d5 tree 7cd8555c192fab712af9e4595df63c3eb2f90aea script .forge/ci 38df37c0d5a6382f60569079a0dd821094e37e4a result pass exit 0 started 2026-10-19T00:34:36Z duration 133s runner (not shown here; refs/notes/ci has it) system Darwin 25.5.0 arm64 env host log sha256:c70d8f21389a0a8e5b132a358420e7c2cde230fb99a078693c26b7dc7a47d866 329 lines |remote: review #4: /dami/greet/reviews/4/ |pushed wip/i18n |eb7cb22 running .forge/ci: greet.sh: say what it is | | sh: /bin/sh | | ok greet |eb7cb22 pass in 0s, signed |pushed main |remote: review #4: /dami/greet/reviews/4/ |pushed wip/i18n |9b6f5b3 running .forge/ci: wave at the world | | sh: /bin/sh | | FAIL greet : got 'hello, world o/', want 'hello, world' |9b6f5b3 fail in 0s, signed |forge: 1 commit(s) did not pass |remote: review #6: /dami/greet/reviews/6/ |pushed ana/wave |e9a69d0 running .forge/ci: update the test for the wave | | sh: /bin/sh | | ok greet |e9a69d0 pass in 0s, signed |remote: review #6: /dami/greet/reviews/6/ |pushed ana/wave |warning: You appear to have cloned an empty repository. |site home: deployed 9cb6494 -> /sites/ana/dotfiles/home/ |created ana/notes |remote: review #1: /ana/notes/reviews/1/ |pushed ana/todo |landing #1 A todo list: ana/todo -> main |a6ae85f running .forge/ci: web, server and Python versions of greet | | sh: /bin/sh | | ok greet |a6ae85f pass in 0s, signed |pushed main |5c69174 running .forge/ci: messages: notes for the later regions | | sh: /bin/sh | | ok greet |5c69174 pass in 0s, signed |remote: review #7: /dami/greet/reviews/7/ |pushed dami/regions |created dami/forge |warning: You appear to have cloned an empty repository. |site guide: deployed 924a1bc -> /sites/dami/forge/guide/ |(node:52394) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built ana/dotfiles in 1s -> /Users/dami/Library/Caches/forge/ci/35f7a65d0235/demo/root/.forge/www |(node:52518) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built ana/notes in 0s -> /Users/dami/Library/Caches/forge/ci/35f7a65d0235/demo/root/.forge/www |(node:52656) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built dami/forge in 1s -> /Users/dami/Library/Caches/forge/ci/35f7a65d0235/demo/root/.forge/www |(node:52804) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built dami/greet in 1s -> /Users/dami/Library/Caches/forge/ci/35f7a65d0235/demo/root/.forge/www |(node:53474) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built - in 0s -> /Users/dami/Library/Caches/forge/ci/35f7a65d0235/demo/root/.forge/www |226 of 226 pages render the same without 11ty |guide: 4 files, links ok |ok
Signature: good, cat
| M | Dockerfile | 18 | +++++++++++++++++- |
| M | README.md | 4 | +++- |
| M | test/ | 8 | ++++---- |
@@ -17,21 +17,37 @@ RUN CGO_ENABLED=0 GOBIN=/out go install github.com/rclone/rclone@v1.75.2
1717 FROM golang:1.27.2 AS litestream1818 RUN CGO_ENABLED=0 GOBIN=/out go install github.com/benbjohnson/litestream/cmd/litestream@v0.5.171919 20+# Caddy serves pages, sites and HTTPS clones. Debian's is 2.6, from 2022, and21+# the edge runs 2.11 (Alpine's, deploy/edge), so build a pinned release of22+# the same line, checked the same way. Built as Caddy's own releases are23+# (trimmed paths, no symbol table: a third smaller); a build of Caddy's own24+# module can't read its version, so caddy version is told it (CustomVersion).25+FROM golang:1.27.2 AS caddy26+RUN v=v2.11.7 && CGO_ENABLED=0 GOBIN=/out go install -trimpath \27+ -ldflags "-s -w -X github.com/caddyserver/caddy/v2.CustomVersion=$v" \28+ github.com/caddyserver/caddy/v2/cmd/caddy@$v29+2030 # Node runs the TypeScript sources directly (type stripping). 24 is the LTS2131 # whose WebCrypto has ML-DSA (24.7 and later), so passkeys can be2232 # post-quantum; the forge itself needs 22.18 or later.2333 FROM node:24-trixie-slim2434 35+# Caddy gets the system user Debian's package would make, in the git group36+# to read pages and repos. media-types is /etc/mime.types, the content37+# types Caddy sends for files beyond the few Go knows itself.2538 RUN apt-get update \26- && apt-get install -y --no-install-recommends git openssh-server caddy ca-certificates tini fcgiwrap restic \39+ && apt-get install -y --no-install-recommends git openssh-server ca-certificates tini fcgiwrap restic media-types \2740 && rm -rf /var/lib/apt/lists/* \2841 && adduser --system --group --shell /bin/sh --home /srv/git git \42+ && adduser --system --group --shell /usr/sbin/nologin --home /var/lib/caddy --gecos 'Caddy web server' caddy \2943 && usermod -aG git caddy \44+ && mkdir -p /etc/caddy \3045 && rm -f /etc/ssh/ssh_host_*3146 3247 COPY --from=tailcat /out/tailcat /usr/local/bin/tailcat3348 COPY --from=rclone /out/rclone /usr/local/bin/rclone3449 COPY --from=litestream /out/litestream /usr/local/bin/litestream50+COPY --from=caddy /out/caddy /usr/local/bin/caddy3551 COPY server/container/sshd.conf /etc/ssh/sshd_config.d/forge.conf3652 3753 WORKDIR /opt/forge@@ -775,7 +775,9 @@ The forge is one container (`Dockerfile`, on Node 24 LTS): sshd for git,
775775 Caddy for pages, sites, and HTTPS clones (with fcgiwrap for `git776776 http-backend`), the events, private and pages servers, litestream, and777777 tailcat. `server/container/start.sh` starts them all. All state is in778-`/srv/git`, a volume.778+`/srv/git`, a volume. Caddy, rclone, litestream and tailcat are pinned779+releases the Dockerfile builds from source; the rest comes from Debian.780+Caddy is on the edge's line, 2.11, where Debian has 2.6.779781 780782 ### Home machine behind a public edge (how src.rest runs)781783 @@ -1,9 +1,9 @@
11 22 # Run the scenarios in the forge's container, as root, the way the forge3-# runs there: Debian's sh (dash), its git, Caddy, restic and litestream, a4-# git user to hand restores to, and no curl. This checkout's code runs with5-# the image's node_modules and DuckDB extensions, so only a change to the6-# Dockerfile or the packages needs a new image.3+# runs there: Debian's sh (dash) and git, the image's Caddy, restic and4+# litestream, a git user to hand restores to, and no curl. This checkout's5+# code runs with the image's node_modules and DuckDB extensions, so only a6+# change to the Dockerfile or the packages needs a new image.77 #88 # docker build -t forge:test . the image, once99 # sh test/container.sh every scenario