detached e914bbd

build(docker): run under tini, pin alpine 3.24 and assert the stamped version in CI

commit
e914bbd46c2af2f7f3d946d56053ef5b9fd61018
author
brice · · 2026-10-10 12:08 UTC
parent
a87ca8b
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +20 -11
M .dockerignore +4 -0
@@ -10,3 +10,7 @@ Dockerfile
1010 /tmp
1111 /dist
1212 tmp
13+/data
14+.env*
15+*.pem
16+*.key
@@ -10,3 +10,7 @@ Dockerfile
10 /tmp10 /tmp
11 /dist11 /dist
12 tmp12 tmp
13+/data
14+.env*
15+*.pem
16+*.key
M .forgejo/workflows/ci.yml +2 -1
@@ -112,6 +112,7 @@ jobs:
112112 # git.bnema.dev through the host's /etc/hosts (127.0.0.1, see vps-infra
113113 # homelab/igor/ci-runner) and reaches Forgejo on :8088. Podman's Docker API rejects
114114 # the classic builder's --add-host encoding, so host networking is used instead.
115+ # Acceptable because only trusted branches of this repo trigger CI (no fork PRs).
115116 - name: Build the image
116117 run: |
117118 docker build \
@@ -121,5 +122,5 @@ jobs:
121122 -t ermite:ci .
122123 - name: Smoke test
123124 run: |
124- docker run --rm ermite:ci version
125+ docker run --rm ermite:ci version | tee /dev/stderr | grep -qF "ermite $(git describe --tags --always --dirty)"
125126 docker run --rm --entrypoint git ermite:ci --version
@@ -112,6 +112,7 @@ jobs:
112 # git.bnema.dev through the host's /etc/hosts (127.0.0.1, see vps-infra112 # git.bnema.dev through the host's /etc/hosts (127.0.0.1, see vps-infra
113 # homelab/igor/ci-runner) and reaches Forgejo on :8088. Podman's Docker API rejects113 # homelab/igor/ci-runner) and reaches Forgejo on :8088. Podman's Docker API rejects
114 # the classic builder's --add-host encoding, so host networking is used instead.114 # the classic builder's --add-host encoding, so host networking is used instead.
115+ # Acceptable because only trusted branches of this repo trigger CI (no fork PRs).
115 - name: Build the image116 - name: Build the image
116 run: |117 run: |
117 docker build \118 docker build \
@@ -121,5 +122,5 @@ jobs:
121 -t ermite:ci .122 -t ermite:ci .
122 - name: Smoke test123 - name: Smoke test
123 run: |124 run: |
124- docker run --rm ermite:ci version125+ docker run --rm ermite:ci version | tee /dev/stderr | grep -qF "ermite $(git describe --tags --always --dirty)"
125 docker run --rm --entrypoint git ermite:ci --version126 docker run --rm --entrypoint git ermite:ci --version
M Dockerfile +9 -6
@@ -7,10 +7,12 @@
77 # The modules bnema.dev/ermite-* are private and resolve through git.bnema.dev, which is
88 # reachable from the NetBird network only. The build container uses the host's DNS and
99 # routes, so on a NetBird host it needs nothing more. Behind a different route (CI), set
10-# --build-arg GIT_REWRITE_BASE=http://git.bnema.dev:8088/ [--add-host git.bnema.dev:<ip>]
10+# --build-arg GIT_REWRITE_BASE=http://git.bnema.dev:8088/ --network host
1111 # to fetch the modules over that URL instead of https://git.bnema.dev/.
12+#
13+# Base images are pinned to an Alpine minor release; bump them on purpose.
1214
13-FROM golang:1.27-alpine AS build
15+FROM golang:1.27-alpine3.24 AS build
1416 RUN apk add --no-cache git
1517 ENV CGO_ENABLED=0 GOWORK=off GOTOOLCHAIN=local GOPRIVATE=bnema.dev/ermite*
1618 ARG GIT_REWRITE_BASE=
@@ -25,9 +27,10 @@ RUN --mount=type=cache,target=/go/pkg/mod \
2527 go build -trimpath -buildvcs=false -ldflags "-s -w -X main.stamped=${VERSION}" \
2628 -o /out/ermite ./cmd/ermite
2729
28-FROM alpine:3
29-# git-daemon provides git-http-backend on alpine.
30-RUN apk add --no-cache git git-daemon ca-certificates \
30+FROM alpine:3.24
31+# git-daemon provides git-http-backend on alpine. tini runs as PID 1: it forwards signals
32+# to ermite and reaps the git processes that detach from pushes (auto maintenance).
33+RUN apk add --no-cache git git-daemon ca-certificates tini \
3134 && adduser -D -u 10001 -h /home/ermite ermite \
3235 && mkdir /data \
3336 && chown ermite:ermite /data
@@ -37,5 +40,5 @@ USER ermite
3740 WORKDIR /data
3841 VOLUME /data
3942 EXPOSE 7341
40-ENTRYPOINT ["/usr/local/bin/ermite"]
43+ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/ermite"]
4144 CMD ["serve"]
@@ -7,10 +7,12 @@
7 # The modules bnema.dev/ermite-* are private and resolve through git.bnema.dev, which is7 # The modules bnema.dev/ermite-* are private and resolve through git.bnema.dev, which is
8 # reachable from the NetBird network only. The build container uses the host's DNS and8 # reachable from the NetBird network only. The build container uses the host's DNS and
9 # routes, so on a NetBird host it needs nothing more. Behind a different route (CI), set9 # routes, so on a NetBird host it needs nothing more. Behind a different route (CI), set
10-# --build-arg GIT_REWRITE_BASE=http://git.bnema.dev:8088/ [--add-host git.bnema.dev:<ip>]10+# --build-arg GIT_REWRITE_BASE=http://git.bnema.dev:8088/ --network host
11 # to fetch the modules over that URL instead of https://git.bnema.dev/.11 # to fetch the modules over that URL instead of https://git.bnema.dev/.
12+#
13+# Base images are pinned to an Alpine minor release; bump them on purpose.
12 14
13-FROM golang:1.27-alpine AS build15+FROM golang:1.27-alpine3.24 AS build
14 RUN apk add --no-cache git16 RUN apk add --no-cache git
15 ENV CGO_ENABLED=0 GOWORK=off GOTOOLCHAIN=local GOPRIVATE=bnema.dev/ermite*17 ENV CGO_ENABLED=0 GOWORK=off GOTOOLCHAIN=local GOPRIVATE=bnema.dev/ermite*
16 ARG GIT_REWRITE_BASE=18 ARG GIT_REWRITE_BASE=
@@ -25,9 +27,10 @@ RUN --mount=type=cache,target=/go/pkg/mod \
25 go build -trimpath -buildvcs=false -ldflags "-s -w -X main.stamped=${VERSION}" \27 go build -trimpath -buildvcs=false -ldflags "-s -w -X main.stamped=${VERSION}" \
26 -o /out/ermite ./cmd/ermite28 -o /out/ermite ./cmd/ermite
27 29
28-FROM alpine:330+FROM alpine:3.24
29-# git-daemon provides git-http-backend on alpine.31+# git-daemon provides git-http-backend on alpine. tini runs as PID 1: it forwards signals
30-RUN apk add --no-cache git git-daemon ca-certificates \32+# to ermite and reaps the git processes that detach from pushes (auto maintenance).
33+RUN apk add --no-cache git git-daemon ca-certificates tini \
31 && adduser -D -u 10001 -h /home/ermite ermite \34 && adduser -D -u 10001 -h /home/ermite ermite \
32 && mkdir /data \35 && mkdir /data \
33 && chown ermite:ermite /data36 && chown ermite:ermite /data
@@ -37,5 +40,5 @@ USER ermite
37 WORKDIR /data40 WORKDIR /data
38 VOLUME /data41 VOLUME /data
39 EXPOSE 734142 EXPOSE 7341
40-ENTRYPOINT ["/usr/local/bin/ermite"]43+ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/ermite"]
41 CMD ["serve"]44 CMD ["serve"]
M README.md +5 -4
@@ -57,11 +57,12 @@ The server keeps its runtime files in `$ERMITE_DATA_DIR/run/` (the hook socket a
5757
5858 ```sh
5959 docker build --build-arg VERSION="$(git describe --tags --always --dirty)" -t ermite .
60-docker run -d -p 7341:7341 -v ermite-data:/data \
61- -e ERMITE_PUBLIC_URL=http://localhost:7341 -e ERMITE_ADMIN_TOKEN="$(head -c 32 /dev/urandom | base64)" ermite
60+token=$(head -c 32 /dev/urandom | base64) # keep it: it is the root credential
61+docker run -d -p 127.0.0.1:7341:7341 -v ermite-data:/data \
62+ -e ERMITE_PUBLIC_URL=http://localhost:7341 -e ERMITE_ADMIN_TOKEN="$token" ermite
6263 ```
6364
64-The image is alpine with git, runs as uid 10001 and keeps its data in the `/data` volume. Logs are JSON by default. The build fetches `bnema.dev/ermite-*` from `git.bnema.dev`, so the host must reach it (NetBird); the build container uses the host's DNS and routes. Behind another route, pass `--build-arg GIT_REWRITE_BASE=http://git.bnema.dev:8088/` (and `--add-host`), as the CI job does.
65+The image is alpine with git, runs as uid 10001 and keeps its data in the `/data` volume. Logs are JSON by default. The build fetches `bnema.dev/ermite-*` from `git.bnema.dev`, so the host must reach it (NetBird); the build container uses the host's DNS and routes. Behind another route, pass `--build-arg GIT_REWRITE_BASE=http://git.bnema.dev:8088/` with `--network host`, as the CI job does.
6566
6667 ## Develop with compose
6768
@@ -69,7 +70,7 @@ The image is alpine with git, runs as uid 10001 and keeps its data in the `/data
6970 docker compose up
7071 ```
7172
72-Serves on `http://localhost:7341` with the development admin token in `compose.yaml`. The source is mounted and [air](https://github.com/air-verse/air) (`go tool air`, `.air.toml`) rebuilds and restarts the server on each change to a `.go` or `.templ` file; the module and build caches and the data live in named volumes. `docker compose down -v` removes them.
73+Serves on `http://localhost:7341` with the development admin token in `compose.yaml`. The source is mounted and [air](https://github.com/air-verse/air) (`go tool air`, `.air.toml`) rebuilds and restarts the server on each change to a `.go` or `.templ` file; the module and build caches and the data live in named volumes. `docker compose down -v` removes them. The dev container runs as root, which maps to your user under rootless Docker.
7374
7475 ## Build
7576
@@ -57,11 +57,12 @@ The server keeps its runtime files in `$ERMITE_DATA_DIR/run/` (the hook socket a
57 57
58 ```sh58 ```sh
59 docker build --build-arg VERSION="$(git describe --tags --always --dirty)" -t ermite .59 docker build --build-arg VERSION="$(git describe --tags --always --dirty)" -t ermite .
60-docker run -d -p 7341:7341 -v ermite-data:/data \60+token=$(head -c 32 /dev/urandom | base64) # keep it: it is the root credential
61- -e ERMITE_PUBLIC_URL=http://localhost:7341 -e ERMITE_ADMIN_TOKEN="$(head -c 32 /dev/urandom | base64)" ermite61+docker run -d -p 127.0.0.1:7341:7341 -v ermite-data:/data \
62+ -e ERMITE_PUBLIC_URL=http://localhost:7341 -e ERMITE_ADMIN_TOKEN="$token" ermite
62 ```63 ```
63 64
64-The image is alpine with git, runs as uid 10001 and keeps its data in the `/data` volume. Logs are JSON by default. The build fetches `bnema.dev/ermite-*` from `git.bnema.dev`, so the host must reach it (NetBird); the build container uses the host's DNS and routes. Behind another route, pass `--build-arg GIT_REWRITE_BASE=http://git.bnema.dev:8088/` (and `--add-host`), as the CI job does.65+The image is alpine with git, runs as uid 10001 and keeps its data in the `/data` volume. Logs are JSON by default. The build fetches `bnema.dev/ermite-*` from `git.bnema.dev`, so the host must reach it (NetBird); the build container uses the host's DNS and routes. Behind another route, pass `--build-arg GIT_REWRITE_BASE=http://git.bnema.dev:8088/` with `--network host`, as the CI job does.
65 66
66 ## Develop with compose67 ## Develop with compose
67 68
@@ -69,7 +70,7 @@ The image is alpine with git, runs as uid 10001 and keeps its data in the `/data
69 docker compose up70 docker compose up
70 ```71 ```
71 72
72-Serves on `http://localhost:7341` with the development admin token in `compose.yaml`. The source is mounted and [air](https://github.com/air-verse/air) (`go tool air`, `.air.toml`) rebuilds and restarts the server on each change to a `.go` or `.templ` file; the module and build caches and the data live in named volumes. `docker compose down -v` removes them.73+Serves on `http://localhost:7341` with the development admin token in `compose.yaml`. The source is mounted and [air](https://github.com/air-verse/air) (`go tool air`, `.air.toml`) rebuilds and restarts the server on each change to a `.go` or `.templ` file; the module and build caches and the data live in named volumes. `docker compose down -v` removes them. The dev container runs as root, which maps to your user under rootless Docker.
73 74
74 ## Build75 ## Build
75 76

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 42

↑↓ moveenter openesc close