# ============================================ # Multi-stage build for security and size # ============================================ # ============================================ # Stage 1: Base Image # ============================================ FROM node:20-alpine AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable # ============================================ # Stage 2: Dependencies # ============================================ FROM base AS dependencies WORKDIR /app # Copy dependency files COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/orchestrator/package.json ./apps/orchestrator/ COPY packages/shared/package.json ./packages/shared/ COPY packages/config/package.json ./packages/config/ # Install production dependencies only RUN pnpm install --frozen-lockfile --prod # ============================================ # Stage 3: Builder # ============================================ FROM base AS builder WORKDIR /app # Copy all source code COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/orchestrator ./apps/orchestrator COPY packages ./packages # Install all dependencies (including dev) RUN pnpm install --frozen-lockfile # Build the application RUN pnpm --filter @mosaic/orchestrator build # ============================================ # Stage 4: Production Runtime # ============================================ FROM node:20-alpine AS runtime # Add metadata labels LABEL maintainer="mosaic-team@mosaicstack.dev" LABEL version="0.0.6" LABEL description="Mosaic Orchestrator - Agent orchestration service" LABEL org.opencontainers.image.source="https://git.mosaicstack.dev/mosaic/stack" LABEL org.opencontainers.image.vendor="Mosaic Stack" LABEL org.opencontainers.image.title="Mosaic Orchestrator" LABEL org.opencontainers.image.description="Agent orchestration service for Mosaic Stack" # Install wget for health checks (if not present) RUN apk add --no-cache wget # Create non-root user and group (node user already exists in alpine) # UID/GID 1000 is the default node user in alpine images WORKDIR /app # Copy built application with proper ownership COPY --from=builder --chown=node:node /app/apps/orchestrator/dist ./dist COPY --from=dependencies --chown=node:node /app/node_modules ./node_modules # Set proper permissions RUN chown -R node:node /app # Switch to non-root user USER node # Expose port EXPOSE 3001 # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1 # Start the application CMD ["node", "dist/main.js"]