# ============================================================================= # Stage 1: Dependencies # Install only production dependencies for optimal layer caching # ============================================================================= FROM oven/bun:1-slim AS dependencies WORKDIR /app # Copy workspace configuration COPY package.json bun.lock ./ # Copy all package.json files to establish workspace structure COPY packages/core/package.json ./packages/core/ COPY packages/api-server/package.json ./packages/api-server/ COPY packages/mcp-server/package.json ./packages/mcp-server/ # Install dependencies with frozen lockfile (production only) RUN bun install --frozen-lockfile --production # ============================================================================= # Stage 2: Build # Build both services with minification for production # ============================================================================= FROM oven/bun:1-slim AS build WORKDIR /app # Copy workspace configuration COPY package.json bun.lock ./ # Copy all package.json files COPY packages/core/package.json ./packages/core/ COPY packages/api-server/package.json ./packages/api-server/ COPY packages/mcp-server/package.json ./packages/mcp-server/ # Install ALL dependencies (including devDependencies for TypeScript) RUN bun install --frozen-lockfile # Copy source code for all packages COPY packages/core ./packages/core COPY packages/api-server ./packages/api-server COPY packages/mcp-server ./packages/mcp-server # Build both services with minification # Output: dist/api/index.js and dist/mcp/index.js RUN bun build ./packages/api-server/src/index.ts \ --target=bun \ --outdir=./dist/api \ --minify && \ bun build ./packages/mcp-server/src/index.ts \ --target=bun \ --outdir=./dist/mcp \ --minify # ============================================================================= # Stage 3: Runtime # Minimal production image with both services # ============================================================================= FROM oven/bun:1-slim AS runtime WORKDIR /app # Copy production dependencies from dependencies stage COPY --from=dependencies /app/node_modules ./node_modules # Copy built artifacts from build stage COPY --from=build /app/dist ./dist # Create cookies directory (will be mounted as volume at runtime) # This ensures the directory exists even if volume is not mounted RUN mkdir -p /app/cookies && \ chown -R bun:bun /app/cookies # Create startup script that runs both services # Uses Bun's built-in capabilities for process management RUN cat > /app/start.sh << 'EOF' #!/bin/bash set -e # Trap SIGTERM and SIGINT for graceful shutdown trap 'echo "Received shutdown signal, stopping services..."; kill -TERM $API_PID $MCP_PID 2>/dev/null; wait' TERM INT # Start API Server in background echo "Starting API Server on port ${API_PORT:-4005}..." bun /app/dist/api/index.js & API_PID=$! # Give API server a moment to initialize sleep 1 # Start MCP Server in background echo "Starting MCP Server on port ${API_PORT:-4006}..." bun /app/dist/mcp/index.js & MCP_PID=$! echo "Both services started successfully" echo "API Server PID: $API_PID" echo "MCP Server PID: $MCP_PID" # Wait for both processes wait $API_PID $MCP_PID EOF RUN chmod +x /app/start.sh # Expose both service ports # API Server: 4005 (default), MCP Server: 4006 (default) EXPOSE 4005 4006 # Environment variables for port configuration ENV PORT=4005 ENV MCP_PORT=4006 # Volume mount point for cookies # Mount your cookies directory here: -v /path/to/cookies:/app/cookies VOLUME ["/app/cookies"] # Health check that verifies both services are responding HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ CMD bun -e "Promise.all([fetch('http://localhost:${PORT}/api/status'),fetch('http://localhost:${MCP_PORT}/.well-known/mcp/server-card.json')]).then(r=>process.exit(0)).catch(()=>process.exit(1))" # Run the startup script CMD ["/app/start.sh"]