44# This Dockerfile builds a production-ready image for DeepTutor
55# containing both the FastAPI backend and Next.js frontend
66#
7- # Build: docker build -t deeptutor .
8- # Run: docker run -p 8001:8001 -p 3782:3782 --env-file .env deeptutor
7+ # Build: docker compose build
8+ # Run: docker compose up -d
9+ #
10+ # Prerequisites:
11+ # 1. Copy .env.example to .env and configure your API keys
12+ # 2. Optionally customize config/main.yaml
913# ============================================
1014
1115# ============================================
@@ -15,6 +19,9 @@ FROM node:20-alpine AS frontend-builder
1519
1620WORKDIR /app/web
1721
22+ # Accept build argument for backend port
23+ ARG BACKEND_PORT=8001
24+
1825# Copy package files first for better caching
1926COPY web/package.json web/package-lock.json* ./
2027
@@ -24,11 +31,12 @@ RUN npm ci --legacy-peer-deps
2431# Copy frontend source code
2532COPY web/ ./
2633
27- # Build the Next.js application
28- # Create a placeholder .env.local for build (will be overridden at runtime)
29- RUN echo "NEXT_PUBLIC_API_BASE=http://localhost:8001 " > .env.local
34+ # Create .env.local with placeholder that will be replaced at runtime
35+ # Use a unique placeholder that can be safely replaced
36+ RUN echo "NEXT_PUBLIC_API_BASE=__NEXT_PUBLIC_API_BASE_PLACEHOLDER__ " > .env.local
3037
31- # Build Next.js for production
38+ # Build Next.js for production with standalone output
39+ # This allows runtime environment variable injection
3240RUN npm run build
3341
3442# ============================================
@@ -46,10 +54,16 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
4654WORKDIR /app
4755
4856# Install system dependencies
57+ # Note: libgl1 and libglib2.0-0 are required for OpenCV (used by mineru)
4958RUN apt-get update && apt-get install -y --no-install-recommends \
5059 curl \
5160 git \
5261 build-essential \
62+ libgl1 \
63+ libglib2.0-0 \
64+ libsm6 \
65+ libxext6 \
66+ libxrender1 \
5367 && rm -rf /var/lib/apt/lists/*
5468
5569# Copy requirements and install Python dependencies
@@ -78,12 +92,18 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
7892
7993WORKDIR /app
8094
81- # Install Node.js 20.x (LTS) and npm
95+ # Install Node.js 20.x (LTS) and required tools
96+ # Note: libgl1 and libglib2.0-0 are required for OpenCV (used by mineru)
8297RUN apt-get update && apt-get install -y --no-install-recommends \
8398 curl \
8499 ca-certificates \
85100 gnupg \
86101 supervisor \
102+ libgl1 \
103+ libglib2.0-0 \
104+ libsm6 \
105+ libxext6 \
106+ libxrender1 \
87107 && mkdir -p /etc/apt/keyrings \
88108 && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
89109 && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
@@ -103,18 +123,14 @@ COPY --from=frontend-builder /app/web/package.json ./web/package.json
103123COPY --from=frontend-builder /app/web/next.config.js ./web/next.config.js
104124COPY --from=frontend-builder /app/web/node_modules ./web/node_modules
105125
106- # Create a startup script for the frontend
107- RUN echo '#!/bin/bash\n cd /app/web && exec node node_modules/next/dist/bin/next start -H 0.0.0.0 -p ${FRONTEND_PORT:-3782}' > /app/start-frontend.sh && \
108- chmod +x /app/start-frontend.sh
109-
110126# Copy application source code
111127COPY src/ ./src/
112128COPY config/ ./config/
113129COPY scripts/ ./scripts/
114130COPY pyproject.toml ./
115131COPY requirements.txt ./
116132
117- # Create necessary directories
133+ # Create necessary directories (these will be overwritten by volume mounts)
118134RUN mkdir -p \
119135 data/user/solve \
120136 data/user/question \
@@ -130,22 +146,25 @@ RUN mkdir -p \
130146 data/knowledge_bases
131147
132148# Create supervisord configuration for running both services
149+ # Log output goes to stdout/stderr so docker logs can capture them
133150RUN mkdir -p /etc/supervisor/conf.d
134151
135152COPY <<EOF /etc/supervisor/conf.d/deeptutor.conf
136153[supervisord]
137154nodaemon=true
138- logfile=/var/log/supervisor/supervisord.log
155+ logfile=/dev/null
156+ logfile_maxbytes=0
139157pidfile=/var/run/supervisord.pid
140- childlogdir=/var/log/supervisor
141158
142159[program:backend]
143- command=python -m uvicorn src.api.main: app --host 0.0.0.0 --port %(ENV_BACKEND_PORT)s
160+ command=/bin/bash / app/start-backend.sh
144161directory=/app
145162autostart=true
146163autorestart=true
147- stderr_logfile=/var/log/supervisor/backend.err.log
148- stdout_logfile=/var/log/supervisor/backend.out.log
164+ stdout_logfile=/dev/fd/1
165+ stdout_logfile_maxbytes=0
166+ stderr_logfile=/dev/fd/2
167+ stderr_logfile_maxbytes=0
149168environment=PYTHONPATH="/app" ,PYTHONUNBUFFERED="1"
150169
151170[program:frontend]
@@ -154,13 +173,63 @@ directory=/app/web
154173autostart=true
155174autorestart=true
156175startsecs=5
157- stderr_logfile=/var/log/supervisor/frontend.err.log
158- stdout_logfile=/var/log/supervisor/frontend.out.log
176+ stdout_logfile=/dev/fd/1
177+ stdout_logfile_maxbytes=0
178+ stderr_logfile=/dev/fd/2
179+ stderr_logfile_maxbytes=0
159180environment=NODE_ENV="production"
160181EOF
161182
162- # Create log directories
163- RUN mkdir -p /var/log/supervisor
183+ # Create backend startup script
184+ COPY <<'EOF' /app/start-backend.sh
185+ # !/bin/bash
186+ set -e
187+
188+ BACKEND_PORT=${BACKEND_PORT:-8001}
189+
190+ echo "[Backend] 🚀 Starting FastAPI backend on port ${BACKEND_PORT}..."
191+
192+ # Run uvicorn directly - the application's logging system already handles:
193+ # 1. Console output (visible in docker logs)
194+ # 2. File logging to data/user/logs/ai_tutor_*.log
195+ exec python -m uvicorn src.api.main:app --host 0.0.0.0 --port ${BACKEND_PORT}
196+ EOF
197+
198+ RUN chmod +x /app/start-backend.sh
199+
200+ # Create frontend startup script
201+ # This script handles runtime environment variable injection for Next.js
202+ COPY <<'EOF' /app/start-frontend.sh
203+ # !/bin/bash
204+ set -e
205+
206+ # Get the backend port (default to 8001)
207+ BACKEND_PORT=${BACKEND_PORT:-8001}
208+ FRONTEND_PORT=${FRONTEND_PORT:-3782}
209+
210+ # Determine the API base URL
211+ if [ -n "$NEXT_PUBLIC_API_BASE_EXTERNAL" ]; then
212+ API_BASE="$NEXT_PUBLIC_API_BASE_EXTERNAL"
213+ else
214+ API_BASE="http://localhost:${BACKEND_PORT}"
215+ fi
216+
217+ echo "[Frontend] 🚀 Starting Next.js frontend on port ${FRONTEND_PORT}..."
218+ echo "[Frontend] 📌 API base URL: ${API_BASE}"
219+
220+ # Replace placeholder in built Next.js files
221+ # This is necessary because NEXT_PUBLIC_* vars are inlined at build time
222+ find /app/web/.next -type f \( -name "*.js" -o -name "*.json" \) -exec \
223+ sed -i "s|__NEXT_PUBLIC_API_BASE_PLACEHOLDER__|${API_BASE}|g" {} \; 2>/dev/null || true
224+
225+ # Also update .env.local for any runtime reads
226+ echo "NEXT_PUBLIC_API_BASE=${API_BASE}" > /app/web/.env.local
227+
228+ # Start Next.js
229+ cd /app/web && exec node node_modules/next/dist/bin/next start -H 0.0.0.0 -p ${FRONTEND_PORT}
230+ EOF
231+
232+ RUN chmod +x /app/start-frontend.sh
164233
165234# Create entrypoint script
166235COPY <<'EOF' /app/entrypoint.sh
@@ -175,24 +244,38 @@ echo "============================================"
175244export BACKEND_PORT=${BACKEND_PORT:-8001}
176245export FRONTEND_PORT=${FRONTEND_PORT:-3782}
177246
178- # Update Next.js environment file with actual backend URL
179- echo "NEXT_PUBLIC_API_BASE=http://localhost:${BACKEND_PORT}" > /app/web/.env.local
180-
181- # If NEXT_PUBLIC_API_BASE is provided externally, use it
182- if [ -n "$NEXT_PUBLIC_API_BASE_EXTERNAL" ]; then
183- echo "NEXT_PUBLIC_API_BASE=${NEXT_PUBLIC_API_BASE_EXTERNAL}" > /app/web/.env.local
184- fi
185-
186247echo "📌 Backend Port: ${BACKEND_PORT}"
187248echo "📌 Frontend Port: ${FRONTEND_PORT}"
188- echo "============================================"
189249
190250# Check for required environment variables
191251if [ -z "$LLM_BINDING_API_KEY" ]; then
192252 echo "⚠️ Warning: LLM_BINDING_API_KEY not set"
193- echo " Please provide LLM configuration via environment variables"
253+ echo " Please provide LLM configuration via environment variables or .env file"
254+ fi
255+
256+ if [ -z "$LLM_MODEL" ]; then
257+ echo "⚠️ Warning: LLM_MODEL not set"
258+ echo " Please configure LLM_MODEL in your .env file"
259+ fi
260+
261+ # Initialize user data directories if empty
262+ echo "📁 Checking data directories..."
263+ if [ ! -f "/app/data/user/user_history.json" ]; then
264+ echo " Initializing user data directories..."
265+ python -c "
266+ from pathlib import Path
267+ from src.core.setup import init_user_directories
268+ init_user_directories(Path('/app'))
269+ " 2>/dev/null || echo " ⚠️ Directory initialization skipped (will be created on first use)"
194270fi
195271
272+ echo "============================================"
273+ echo "📦 Configuration loaded from:"
274+ echo " - Environment variables (.env file)"
275+ echo " - config/main.yaml"
276+ echo " - config/agents.yaml"
277+ echo "============================================"
278+
196279# Start supervisord
197280exec /usr/bin/supervisord -c /etc/supervisor/conf.d/deeptutor.conf
198281EOF
@@ -227,20 +310,23 @@ RUN pip install --no-cache-dir \
227310 ruff
228311
229312# Override supervisord config for development (with reload)
313+ # Log output goes to stdout/stderr so docker logs can capture them
230314COPY <<EOF /etc/supervisor/conf.d/deeptutor.conf
231315[supervisord]
232316nodaemon=true
233- logfile=/var/log/supervisor/supervisord.log
317+ logfile=/dev/null
318+ logfile_maxbytes=0
234319pidfile=/var/run/supervisord.pid
235- childlogdir=/var/log/supervisor
236320
237321[program:backend]
238322command=python -m uvicorn src.api.main:app --host 0.0.0.0 --port %(ENV_BACKEND_PORT)s --reload
239323directory=/app
240324autostart=true
241325autorestart=true
242- stderr_logfile=/var/log/supervisor/backend.err.log
243- stdout_logfile=/var/log/supervisor/backend.out.log
326+ stdout_logfile=/dev/fd/1
327+ stdout_logfile_maxbytes=0
328+ stderr_logfile=/dev/fd/2
329+ stderr_logfile_maxbytes=0
244330environment=PYTHONPATH="/app" ,PYTHONUNBUFFERED="1"
245331
246332[program:frontend]
@@ -249,8 +335,10 @@ directory=/app/web
249335autostart=true
250336autorestart=true
251337startsecs=5
252- stderr_logfile=/var/log/supervisor/frontend.err.log
253- stdout_logfile=/var/log/supervisor/frontend.out.log
338+ stdout_logfile=/dev/fd/1
339+ stdout_logfile_maxbytes=0
340+ stderr_logfile=/dev/fd/2
341+ stderr_logfile_maxbytes=0
254342environment=NODE_ENV="development"
255343EOF
256344
0 commit comments