Internal
Public Access
Changed pip upgrade to use --user flag so the upgraded pip is installed to /root/.local and gets copied to the runtime stage. Previous version upgraded pip globally in builder stage but runtime stage was still using base image's pip (25.0.1). Now the upgraded pip is in .local/bin which is in the PATH in the runtime container. This fixes CVE-2025-8869 in pip. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
70 lines
2.1 KiB
Docker
70 lines
2.1 KiB
Docker
# ===================================================================
|
|
# Stage 1: Builder - Install dependencies
|
|
# ===================================================================
|
|
FROM python:3.12-slim-bookworm AS builder
|
|
|
|
# Install build dependencies for PostgreSQL and other packages
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
gcc \
|
|
libpq-dev \
|
|
libjpeg-dev \
|
|
zlib1g-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Upgrade pip to latest version (security) - install to user directory
|
|
RUN pip install --user --upgrade pip
|
|
|
|
# Install Python dependencies
|
|
COPY requirements.txt /tmp/
|
|
RUN pip install --user --no-cache-dir -r /tmp/requirements.txt \
|
|
&& pip install --user --no-cache-dir gunicorn
|
|
|
|
# ===================================================================
|
|
# Stage 2: Runtime - Minimal production image
|
|
# ===================================================================
|
|
FROM python:3.12-slim-bookworm
|
|
|
|
# Install runtime dependencies only
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
postgresql-client \
|
|
curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Create django user (non-root for security)
|
|
RUN useradd -m -u 1000 -s /bin/bash django
|
|
|
|
# Set working directory
|
|
WORKDIR /app
|
|
|
|
# Copy Python dependencies from builder stage
|
|
COPY --from=builder /root/.local /home/django/.local
|
|
|
|
# Copy application code
|
|
COPY --chown=django:django . /app/
|
|
|
|
# Create directories for media and static files
|
|
RUN mkdir -p /app/media /app/staticfiles && \
|
|
chown -R django:django /app/media /app/staticfiles
|
|
|
|
# Switch to django user
|
|
USER django
|
|
|
|
# Add .local/bin to PATH for installed packages
|
|
ENV PATH=/home/django/.local/bin:$PATH
|
|
|
|
# Set Python to run in unbuffered mode (better for Docker logs)
|
|
ENV PYTHONUNBUFFERED=1
|
|
|
|
# Expose port 8000 for Gunicorn
|
|
EXPOSE 8000
|
|
|
|
# Health check - ping the API endpoint
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
CMD curl -f http://localhost:8000/api/ || exit 1
|
|
|
|
# Entrypoint script handles migrations, static collection, and service startup
|
|
ENTRYPOINT ["/app/docker/entrypoint.sh"]
|
|
|
|
# Default command is to run Gunicorn
|
|
CMD ["gunicorn"]
|