# fleet-manager — server-side fleet control plane + embedded admin SPA. # # Two stages: build the Svelte SPA (node) → dist/, then compile the Go binary # with the SPA embedded (`-tags embedspa`). The gRPC .pb.go stubs are committed, # so no protoc is needed here. Ships as a small static distroless image; the same # binary also serves the `register-admin` subcommand. # # docker build -t fleet-manager serving/fleet-manager # ---- 1. build the SPA ------------------------------------------------------ FROM harbor.bitasylum.org/dockerhub/library/node:24-alpine AS ui WORKDIR /ui COPY ui/package.json ui/package-lock.json ./ RUN npm ci --no-audit --no-fund COPY ui/ ./ RUN npm run build # -> /ui/dist # ---- 2. build the Go binary (SPA embedded) -------------------------------- FROM harbor.bitasylum.org/dockerhub/library/golang:1.26-alpine AS build WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . COPY --from=ui /ui/dist ./ui/dist ENV CGO_ENABLED=0 GOTOOLCHAIN=local RUN go build -tags embedspa -trimpath -buildvcs=false \ -ldflags '-s -w -buildid=' -o /fleet-manager . # Seed dir whose ownership a FRESH named volume inherits via Docker's copy-up, so # the nonroot runtime (uid 65532) can write inventory/admins/enroll.key under # /data without a manual chown. (An ALREADY-created root-owned volume must still # be chowned or recreated once — see DEPLOY.md.) RUN mkdir -p /data-seed && touch /data-seed/.keep # ---- 3. minimal runtime ---------------------------------------------------- # distroless/static: static binary + ca-certificates + tzdata, non-root by # default (uid 65532). The data dir (inventory + admin store + enrol key) is a # mounted, writable volume — pre-created here owned by nonroot so a fresh volume # inherits that ownership. FROM gcr.io/distroless/static-debian12:nonroot COPY --from=build /fleet-manager /usr/local/bin/fleet-manager COPY --from=build --chown=65532:65532 /data-seed /data USER nonroot:nonroot ENTRYPOINT ["/usr/local/bin/fleet-manager"]