# ── 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 all source code needed for build COPY client/ ./client/ COPY server/ ./server/ COPY shared/ ./shared/ COPY drizzle/ ./drizzle/ COPY drizzle.config.ts tsconfig.json tsconfig.node.json vite.config.ts vitest.config.ts ./ # Build frontend (Vite) and backend (esbuild) RUN pnpm build # ── Runtime Stage ───────────────────────────────────────────────────────────── FROM node:22-alpine # Install runtime dependencies including Docker CLI for container management RUN apk add --no-cache \ wget \ curl \ bash \ netcat-openbsd \ && curl -fsSL "https://download.docker.com/linux/static/stable/x86_64/docker-27.5.1.tgz" -o /tmp/docker.tgz \ && tar -xzf /tmp/docker.tgz -C /tmp \ && mv /tmp/docker/docker /usr/local/bin/docker \ && rm -rf /tmp/docker.tgz /tmp/docker \ && rm -rf /var/cache/apk/* 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 (drizzle-kit needed for migrations at runtime) RUN pnpm install --frozen-lockfile --ignore-scripts # Copy built artifacts from builder COPY --from=builder /app/dist ./dist # Copy drizzle config and migrations for runtime migration COPY drizzle.config.ts ./drizzle.config.ts COPY drizzle/ ./drizzle/ # Copy entrypoint script COPY docker/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh # Run as non-root user with docker access (GID 988 matches host docker group) RUN addgroup -g 1001 goclaw && \ addgroup -g 988 docker-host && \ adduser -u 1001 -G goclaw -s /bin/sh -D goclaw && \ addgroup goclaw docker-host && \ chown -R goclaw:goclaw /app USER goclaw EXPOSE 3000 # Health check HEALTHCHECK --interval=15s --timeout=5s --start-period=30s --retries=3 \ CMD wget -qO- http://localhost:3000/api/health || exit 1 ENTRYPOINT ["/entrypoint.sh"]