# ───────────────────────────────────────────── # Stage 1: Build the React frontend # ───────────────────────────────────────────── FROM node:20-alpine AS ui-builder WORKDIR /app # Install dependencies first (better layer caching) COPY package.json package-lock.json* ./ RUN npm ci # Copy source and build COPY index.html vite.config.ts tsconfig.json tsconfig.node.json tailwind.config.js postcss.config.js* ./ COPY src ./src RUN npm run build # Output lands in backend/cmd/moonrelay/ui/ (per vite.config.ts) # ───────────────────────────────────────────── # Stage 2: Build the Go backend # ───────────────────────────────────────────── FROM golang:1.22-alpine AS go-builder WORKDIR /app # System deps for tsnet (WireGuard needs these on Linux) RUN apk add --no-cache git COPY backend/go.mod backend/go.sum ./backend/ RUN cd backend && go mod download # Copy all Go source COPY backend ./backend # Copy the compiled React SPA into the go:embed target directory COPY --from=ui-builder /app/backend/cmd/moonrelay/ui ./backend/cmd/moonrelay/ui # Build a static binary RUN cd backend && CGO_ENABLED=0 GOOS=linux go build \ -ldflags="-s -w" \ -o /moonrelay \ ./cmd/moonrelay # ───────────────────────────────────────────── # Stage 3: Minimal runtime image # ───────────────────────────────────────────── FROM alpine:3.19 # ca-certificates needed for Tailscale TLS RUN apk add --no-cache ca-certificates tzdata # Create a non-root user RUN addgroup -S moonrelay && adduser -S moonrelay -G moonrelay # Persistent state directory (mount a volume here) RUN mkdir -p /data && chown moonrelay:moonrelay /data COPY --from=go-builder /moonrelay /usr/local/bin/moonrelay USER moonrelay # Web UI port EXPOSE 8080 # Tailscale state lives here — mount a named volume for persistence ENV TS_STATE_DIR=/data/tsnet ENV MOONRELAY_PORT=8080 ENV MOONRELAY_HOST=0.0.0.0 ENTRYPOINT ["/usr/local/bin/moonrelay"]