# Shared multi-stage Dockerfile for all NestJS microservices # Usage: docker build --build-arg SERVICE_NAME=auth-service --build-arg SERVICE_PORT=3001 -f Dockerfile.service . # ===== Build Stage ===== FROM node:18-alpine AS builder RUN corepack enable WORKDIR /app # Copy workspace configuration and lockfile (for dependency caching) COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json tsconfig.base.json ./ # Copy all package.json files first (Docker layer cache: deps reinstall only when these change) COPY packages/shared/common/package.json packages/shared/common/ COPY packages/shared/database/package.json packages/shared/database/ COPY packages/shared/events/package.json packages/shared/events/ COPY packages/shared/proto/package.json packages/shared/proto/ COPY packages/shared/testing/package.json packages/shared/testing/ COPY packages/services/auth-service/package.json packages/services/auth-service/ COPY packages/services/agent-service/package.json packages/services/agent-service/ COPY packages/services/ops-service/package.json packages/services/ops-service/ COPY packages/services/inventory-service/package.json packages/services/inventory-service/ COPY packages/services/monitor-service/package.json packages/services/monitor-service/ COPY packages/services/comm-service/package.json packages/services/comm-service/ COPY packages/services/audit-service/package.json packages/services/audit-service/ COPY packages/services/billing-service/package.json packages/services/billing-service/ COPY packages/services/version-service/package.json packages/services/version-service/ COPY packages/services/presence-service/package.json packages/services/presence-service/ # Install all dependencies (cached unless package.json changes) RUN pnpm install --frozen-lockfile # Copy source code and tsconfig files COPY packages/shared/ packages/shared/ COPY packages/services/ packages/services/ # Build all shared libraries + target service ARG SERVICE_NAME RUN pnpm turbo build --filter='./packages/shared/*' --filter=@it0/${SERVICE_NAME} # ===== Production Stage ===== FROM node:18-alpine # Install bash (required by Claude Agent SDK Bash tool) + openssh-client (for SSH to managed servers) + su-exec (for privilege drop) RUN apk add --no-cache bash openssh-client su-exec RUN corepack enable WORKDIR /app # Copy workspace config and all package.jsons (required for pnpm workspace resolution) COPY --from=builder /app/package.json /app/pnpm-lock.yaml /app/pnpm-workspace.yaml ./ COPY --from=builder /app/packages/shared/common/package.json packages/shared/common/ COPY --from=builder /app/packages/shared/database/package.json packages/shared/database/ COPY --from=builder /app/packages/shared/events/package.json packages/shared/events/ COPY --from=builder /app/packages/shared/proto/package.json packages/shared/proto/ COPY --from=builder /app/packages/shared/testing/package.json packages/shared/testing/ COPY --from=builder /app/packages/services/auth-service/package.json packages/services/auth-service/ COPY --from=builder /app/packages/services/agent-service/package.json packages/services/agent-service/ COPY --from=builder /app/packages/services/ops-service/package.json packages/services/ops-service/ COPY --from=builder /app/packages/services/inventory-service/package.json packages/services/inventory-service/ COPY --from=builder /app/packages/services/monitor-service/package.json packages/services/monitor-service/ COPY --from=builder /app/packages/services/comm-service/package.json packages/services/comm-service/ COPY --from=builder /app/packages/services/audit-service/package.json packages/services/audit-service/ COPY --from=builder /app/packages/services/billing-service/package.json packages/services/billing-service/ COPY --from=builder /app/packages/services/version-service/package.json packages/services/version-service/ COPY --from=builder /app/packages/services/presence-service/package.json packages/services/presence-service/ # Install production dependencies only RUN pnpm install --frozen-lockfile --prod # Copy built shared libraries (dist/ produced by tsc) COPY --from=builder /app/packages/shared/common/dist packages/shared/common/dist/ COPY --from=builder /app/packages/shared/database/dist packages/shared/database/dist/ COPY --from=builder /app/packages/shared/events/dist packages/shared/events/dist/ COPY --from=builder /app/packages/shared/proto/dist packages/shared/proto/dist/ # Copy target service build output ARG SERVICE_NAME COPY --from=builder /app/packages/services/${SERVICE_NAME}/dist packages/services/${SERVICE_NAME}/dist/ # Copy SQL migration templates alongside compiled shared code in each service dist COPY --from=builder /app/packages/shared/database/src/migrations packages/services/${SERVICE_NAME}/dist/shared/database/src/migrations/ # Copy Prisma generated client (required for services using @prisma/client) # prisma generate runs in builder stage; production pnpm install skips build scripts COPY --from=builder /app/node_modules/.prisma /app/node_modules/.prisma WORKDIR /app/packages/services/${SERVICE_NAME} # Create non-root user for service runtime RUN adduser -D -h /home/appuser appuser && \ mkdir -p /data/claude-tenants && \ mkdir -p /data/versions/android /data/versions/ios && \ mkdir -p /home/appuser/.ssh && chmod 700 /home/appuser/.ssh && \ chown -R appuser:appuser /app /data/claude-tenants /data/versions /home/appuser/.ssh # Copy entrypoint script (runs as root to fix SSH key permissions, then drops to appuser) COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ARG SERVICE_PORT=3000 EXPOSE ${SERVICE_PORT} ENV SERVICE_NAME=${SERVICE_NAME} ENV NODE_OPTIONS="--experimental-global-webcrypto" ENV SHELL=/bin/bash ENTRYPOINT ["/entrypoint.sh"]