docker
Dockerfile for Python
Production-ready Dockerfile for Python applications with virtual environments, slim base image, and security hardening.
Overview
A well-structured Dockerfile for Python applications (Flask, FastAPI, Django, etc.) using a slim base image, virtual environment isolation, non-root user, and optimized layer caching for fast rebuilds.
Configuration
# Dockerfile
# ── Build Stage ──
FROM python:3.12-slim AS builder
# Prevent Python from writing .pyc files and buffering stdout/stderr
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# Install system dependencies for building Python packages
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# Create virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# ── Production Stage ──
FROM python:3.12-slim AS production
# Runtime environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PATH="/opt/venv/bin:$PATH"
WORKDIR /app
# Install only runtime system dependencies (no build tools)
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
curl \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r appgroup && useradd -r -g appgroup -d /app appuser
# Copy virtual environment from builder
COPY --from=builder /opt/venv /opt/venv
# Copy application source code
COPY . .
# Set ownership to non-root user
RUN chown -R appuser:appgroup /app
# Switch to non-root user
USER appuser
# Expose application port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Run with Gunicorn (for Flask/Django) or Uvicorn (for FastAPI)
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000", "--workers", "4", "--threads", "2", "--timeout", "120"]
Key Options Explained
python:3.12-slim— Slim variant is ~150MB vs ~900MB for the full image. Includes just enough for most Python apps.- Virtual environment (
/opt/venv) — Isolates dependencies and makes them easy to copy between stages without carrying build tools. PYTHONDONTWRITEBYTECODE=1— Prevents.pycfile generation, reducing image size and avoiding stale bytecode issues.PYTHONUNBUFFERED=1— Ensuresprint()and logging output appears immediately indocker logsinstead of being buffered.--no-cache-dir— Tells pip not to store downloaded packages in cache, reducing the final image size.libpq-devvslibpq5— Build stage needs development headers (-dev) for compilingpsycopg2. Runtime only needs the shared library (libpq5).- Multi-stage build — Build tools (
build-essential,gcc) exist only in the builder stage. The production image never contains them.
Common Modifications
- FastAPI with Uvicorn: Replace CMD with
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]. - Poetry: Replace
requirements.txtwithCOPY pyproject.toml poetry.lock ./andRUN pip install poetry && poetry install --no-dev --no-interaction. - pip-tools: Use
pip-compile requirements.inlocally, then copyrequirements.txtinto the builder for deterministic installs. - Add static files: For Django, add
RUN python manage.py collectstatic --noinputbefore switching to non-root user. - Development target: Add a
FROM builder AS developmentstage that includes dev dependencies and runs with--reloadflag.