# Multi-stage Dockerfile for agentic-rag project # Includes both Python service and Next.js web frontend # Stage 1: Build web frontend FROM node:18-alpine AS web-builder WORKDIR /app/web # Install pnpm first with official registry RUN npm install -g pnpm # Use Taobao mirror for package installation (more complete than Tsinghua) RUN npm config set registry https://registry.npmmirror.com && \ pnpm config set registry https://registry.npmmirror.com # Copy web dependencies and install COPY web/package.json ./ RUN pnpm install # Copy web source and build COPY web/ . ENV NEXT_TELEMETRY_DISABLED=1 RUN pnpm build # Stage 2: Final runtime image FROM python:3.12-slim # Use Tsinghua mirror for Debian packages RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list.d/debian.sources # Install system dependencies RUN apt-get update && apt-get install -y \ curl \ xz-utils \ && rm -rf /var/lib/apt/lists/* # Install Node.js for running web app (using direct binary from Tsinghua mirror) RUN curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/nodejs-release/v22.16.0/node-v22.16.0-linux-x64.tar.xz -o node.tar.xz \ && tar -xf node.tar.xz -C /usr/local --strip-components=1 \ && rm node.tar.xz # Install uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv # Set work directory WORKDIR /app # Copy Python project files COPY pyproject.toml uv.lock ./ COPY README.md ./ COPY service/ service/ # Install Python dependencies ENV UV_COMPILE_BYTECODE=1 ENV UV_LINK_MODE=copy ENV UV_CACHE_DIR=/home/appuser/.cache/uv ENV PYTHONPATH=/app RUN uv sync --frozen --no-dev --no-install-workspace # Copy built web app from builder stage COPY --from=web-builder /app/web/.next/standalone ./web/ COPY --from=web-builder /app/web/.next/static ./web/.next/static COPY --from=web-builder /app/web/public ./web/public # Create non-root user RUN groupadd -r appuser && useradd -r -g appuser -m appuser RUN chown -R appuser:appuser /app # Create and set permissions for uv cache directory RUN mkdir -p /home/appuser/.cache && chown -R appuser:appuser /home/appuser/.cache USER appuser # Expose ports EXPOSE 3000 8000 # Create startup script RUN echo '#!/bin/bash' > /app/start.sh && \ echo 'set -e' >> /app/start.sh && \ echo '' >> /app/start.sh && \ echo '# Start Python service in background' >> /app/start.sh && \ echo 'echo "Starting Python service..."' >> /app/start.sh && \ echo '.venv/bin/uvicorn service.main:app --host 0.0.0.0 --port 8000 &' >> /app/start.sh && \ echo 'PID1=$!' >> /app/start.sh && \ echo '' >> /app/start.sh && \ echo '# Start Next.js web app' >> /app/start.sh && \ echo 'echo "Starting web app..."' >> /app/start.sh && \ echo 'cd /app/web' >> /app/start.sh && \ echo 'node server.js &' >> /app/start.sh && \ echo 'PID2=$!' >> /app/start.sh && \ echo '' >> /app/start.sh && \ echo '# Wait for any process to exit' >> /app/start.sh && \ echo 'wait -n' >> /app/start.sh && \ echo '' >> /app/start.sh && \ echo '# Exit with status 1 if any process fails' >> /app/start.sh && \ echo 'exit 1' >> /app/start.sh && \ chown appuser:appuser /app/start.sh && \ chmod +x /app/start.sh # Health check HEALTHCHECK --interval=30s --timeout=30s --start-period=40s --retries=3 \ CMD curl -f http://localhost:8000/health && curl -f http://localhost:3000/api/health || exit 1 # Start both services CMD ["/app/start.sh"]