# git-forge: one binary, one data directory, one listener. Strictly Alpine, # both stages, so there is one distribution to reason about. FROM alpine:3.23 AS build RUN apk add --no-cache go WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . # Static, so the runtime image needs neither Go nor libc. RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /git-forge . FROM alpine:3.23 # git-daemon is not the daemon here: in Alpine that subpackage is where # git-http-backend lives, and without it every clone and push answers 500. # tini reaps what git leaves: a detached gc is reparented to PID 1 and would # otherwise stay a zombie. RUN apk add --no-cache git git-daemon cgit highlight ca-certificates tini COPY --from=build /git-forge /usr/local/bin/git-forge # Non-root, and the datadir is credential material, so 0700 and one owner. RUN adduser -D -H -u 1000 forge \ && install -d -o forge -g forge -m 0700 /var/lib/git-forge USER forge ENV HOME=/tmp EXPOSE 8080 VOLUME /var/lib/git-forge HEALTHCHECK --interval=30s --timeout=5s CMD wget -qO- http://127.0.0.1:8080/healthz || exit 1 ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/git-forge"]