# ── Build Stage ────────────────────────────────────────────────────────────── FROM node:22-alpine AS builder # Install pnpm RUN npm install -g pnpm@latest WORKDIR /app # Copy package files and patches for layer caching COPY package.json pnpm-lock.yaml ./ COPY patches/ ./patches/ RUN pnpm install --frozen-lockfile # Copy source code (exclude gateway/ and docker/) COPY client/ ./client/ COPY server/ ./server/ COPY shared/ ./shared/ COPY drizzle/ ./drizzle/ COPY drizzle.config.ts tsconfig.json vite.config.ts ./ # Build frontend and backend RUN pnpm build # ── Runtime Stage ───────────────────────────────────────────────────────────── FROM node:22-alpine # Install runtime dependencies RUN apk add --no-cache \ wget \ curl \ && rm -rf /var/cache/apk/* # Create non-root user RUN addgroup -g 1001 goclaw && \ adduser -u 1001 -G goclaw -s /bin/sh -D goclaw WORKDIR /app # Install pnpm RUN npm install -g pnpm@latest # Copy package files and install production deps only COPY package.json pnpm-lock.yaml ./ COPY patches/ ./patches/ # Install all deps (vite is needed at runtime for SSR/proxy) RUN pnpm install --frozen-lockfile --ignore-scripts # Copy built artifacts COPY --from=builder /app/dist ./dist COPY --from=builder /app/drizzle ./drizzle # Set ownership RUN chown -R goclaw:goclaw /app USER goclaw EXPOSE 3000 # Health check HEALTHCHECK --interval=15s --timeout=5s --start-period=20s --retries=3 \ CMD wget -qO- http://localhost:3000/api/health || exit 1 CMD ["node", "dist/index.js"]