# Multi-stage for true single binary with embedded Svelte UI # 1. Build Svelte UI (if ui/ present) FROM node:20-alpine AS ui-builder WORKDIR /src # Copy the entire context (cheap for this project size at this stage) COPY . . # Always ensure a dist exists RUN mkdir -p ui/dist && printf '
UI placeholder (run make ui-build for the real Svelte SPA)
' > ui/dist/index.html # If a real ui/ with package.json was copied, build it (overwrites the placeholder) RUN if [ -f ui/package.json ]; then \ cd ui && npm ci && npm run build; \ fi # Put the final dist where the Go embed step (ui/dist from module root) will find it RUN mkdir -p /out-ui/dist && cp -r ui/dist/* /out-ui/dist/ 2>/dev/null || cp -r ui/dist /out-ui/ 2>/dev/null || true # 2. Build Go binary (embeds the dist produced above or the placeholder) FROM golang:1.25-alpine AS go-builder WORKDIR /src RUN apk add --no-cache git ca-certificates COPY go.mod go.sum ./ RUN go mod download COPY . . # Copy the UI dist produced by previous stage into the location the embed expects COPY --from=ui-builder /out-ui/dist ./ui/dist RUN TAG="$$(git describe --tags --exact-match HEAD 2>/dev/null || true)" && \ COMMIT="$$(git rev-parse --short HEAD 2>/dev/null || true)" && \ CGO_ENABLED=0 go build -ldflags="-s -w -X helix-proxy/internal/version.Version=$$TAG -X helix-proxy/internal/version.Commit=$$COMMIT" -o /out/helix-proxy ./cmd/helix-proxy # Final: minimal image, single binary FROM alpine:3.20 RUN apk add --no-cache ca-certificates tzdata wget && \ adduser -D -u 1000 -g 1000 appuser WORKDIR /app COPY --from=go-builder /out/helix-proxy /app/helix-proxy # The binary contains the UI via embed. It will create ./data (and data/www) relative to CWD on first run. USER appuser EXPOSE 80 81 443 ENTRYPOINT ["/app/helix-proxy"] # In practice run with: # docker run -p 80:80 -p 81:81 -p 443:443 -v $PWD/data:/app/data --workdir /app helix-proxy:dev # (or set DATA_DIR etc. to move things)