# Stage 1: Build FROM node:22.12-alpine3.21 AS builder # Install build dependencies (bash needed for lexicon glob expansion) RUN apk add --no-cache bash # Install pnpm RUN npm install -g pnpm@9.15.4 # Set working directory WORKDIR /build # Copy package files for dependency installation COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/appview/package.json ./apps/appview/ COPY apps/web/package.json ./apps/web/ COPY packages/db/package.json ./packages/db/ COPY packages/atproto/package.json ./packages/atproto/ COPY packages/cli/package.json ./packages/cli/ COPY packages/lexicon/package.json ./packages/lexicon/ COPY packages/logger/package.json ./packages/logger/ # Install all dependencies (including dev dependencies for build) # Skip prepare script (lefthook requires git, which we don't need in Docker) RUN pnpm install --frozen-lockfile --ignore-scripts # Copy source code (.dockerignore filters out unwanted files) COPY . . # Build all packages (turbo builds lexicon → appview + web) RUN pnpm build # Stage 2: Runtime FROM node:22.12-alpine3.21 # Install nginx and wget (for health checks) RUN apk add --no-cache nginx wget # Set working directory WORKDIR /app # Copy package files for production install COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/appview/package.json ./apps/appview/ COPY apps/web/package.json ./apps/web/ COPY packages/db/package.json ./packages/db/ COPY packages/atproto/package.json ./packages/atproto/ COPY packages/cli/package.json ./packages/cli/ COPY packages/lexicon/package.json ./packages/lexicon/ COPY packages/logger/package.json ./packages/logger/ # Install pnpm and production dependencies only # Skip prepare script (lefthook not needed in production) RUN npm install -g pnpm@9.15.4 && \ pnpm install --prod --frozen-lockfile --ignore-scripts # Create non-root user and set permissions RUN addgroup -g 1001 -S atbb && \ adduser -S -D -H -u 1001 -h /app -s /sbin/nologin -G atbb atbb && \ chown -R atbb:atbb /app # Give nginx permissions to run as non-root RUN mkdir -p /var/lib/nginx /var/log/nginx /run/nginx && \ chown -R atbb:atbb /var/lib/nginx /var/log/nginx /run/nginx # Copy built artifacts from builder stage COPY --from=builder /build/apps/appview/dist ./apps/appview/dist COPY --from=builder /build/apps/web/dist ./apps/web/dist COPY --from=builder /build/packages/db/dist ./packages/db/dist COPY --from=builder /build/packages/atproto/dist ./packages/atproto/dist COPY --from=builder /build/packages/cli/dist ./packages/cli/dist COPY --from=builder /build/packages/lexicon/dist ./packages/lexicon/dist COPY --from=builder /build/packages/logger/dist ./packages/logger/dist # Copy migration files for drizzle-kit COPY --from=builder /build/apps/appview/drizzle ./apps/appview/drizzle # Copy nginx config to standard location and entrypoint script COPY nginx.conf /etc/nginx/nginx.conf COPY entrypoint.sh ./ RUN chmod +x entrypoint.sh # Expose port 80 (nginx listens here) EXPOSE 80 # Add health check for container orchestration HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost/api/healthz || exit 1 # Switch to non-root user USER atbb # Set entrypoint ENTRYPOINT ["/app/entrypoint.sh"]