# Multi-stage Dockerfile for Radar # # Usage: # Full build (default): docker build . # Release (pre-built): docker build --target release . # (requires radar-amd64/radar-arm64 binaries in context) # ============================================================================= # Stage 1: Build frontend # ============================================================================= FROM node:24-alpine AS frontend-builder WORKDIR /app # Install dependencies (workspace root + all packages) COPY package*.json ./ COPY web/package*.json ./web/ COPY packages/k8s-ui/package*.json ./packages/k8s-ui/ RUN npm ci --prefer-offline --no-audit # Build frontend COPY web/ ./web/ COPY packages/k8s-ui/ ./packages/k8s-ui/ RUN npm run build --workspace=web # ============================================================================= # Stage 2: Build Go backend # ============================================================================= FROM golang:1.26-alpine AS backend-builder # Install build dependencies RUN apk add --no-cache git ca-certificates WORKDIR /app # Download Go modules first (cacheable layer) COPY go.mod go.sum ./ COPY pkg/ pkg/ RUN go mod download # Copy source code COPY cmd/ cmd/ COPY internal/ internal/ # Copy built frontend into embed location COPY --from=frontend-builder /app/web/dist internal/static/dist/ # Build arguments # TARGETOS and TARGETARCH are automatically set by Docker buildx for multi-platform builds # Defaults provided for regular docker build (without buildx) ARG VERSION=dev ARG TARGETOS=linux ARG TARGETARCH=amd64 ARG GOEXPERIMENT="" # Build the binary RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOEXPERIMENT=${GOEXPERIMENT} \ go build -ldflags "-s -w -X main.version=${VERSION}" \ -o /radar ./cmd/explorer # ============================================================================= # Stage 3a: Full build (default) - copies from build stages # ============================================================================= FROM gcr.io/distroless/static-debian12:nonroot AS full LABEL org.opencontainers.image.title="Radar" LABEL org.opencontainers.image.description="Modern Kubernetes visibility — topology, traffic, and Helm management" LABEL org.opencontainers.image.source="https://github.com/skyhook-io/radar" LABEL org.opencontainers.image.vendor="Skyhook" # Ownership proof for the official MCP registry — must match the name in server.json. LABEL io.modelcontextprotocol.server.name="io.github.skyhook-io/radar" COPY --from=backend-builder /radar /radar EXPOSE 9280 USER nonroot:nonroot # Keep the container networking invariant in ENTRYPOINT so docker run arguments # and Compose command overrides cannot silently replace the shared listener. ENTRYPOINT ["/radar", "--listen-address=0.0.0.0"] CMD ["--no-browser"] # ============================================================================= # Stage 3b: Release build - uses pre-built binaries from goreleaser # Much faster for multi-arch since no QEMU compilation needed # Requires: radar-amd64 and radar-arm64 in build context # ============================================================================= FROM gcr.io/distroless/static-debian12:nonroot AS release LABEL org.opencontainers.image.title="Radar" LABEL org.opencontainers.image.description="Modern Kubernetes visibility — topology, traffic, and Helm management" LABEL org.opencontainers.image.source="https://github.com/skyhook-io/radar" LABEL org.opencontainers.image.vendor="Skyhook" # Ownership proof for the official MCP registry — must match the name in server.json. LABEL io.modelcontextprotocol.server.name="io.github.skyhook-io/radar" ARG TARGETARCH COPY radar-${TARGETARCH} /radar EXPOSE 9280 USER nonroot:nonroot # Keep the container networking invariant in ENTRYPOINT so docker run arguments # and Compose command overrides cannot silently replace the shared listener. ENTRYPOINT ["/radar", "--listen-address=0.0.0.0"] CMD ["--no-browser"]