A Practical Multi-Stage Docker Build for Python APIs
When containerizing Python web applications for production, developers often produce heavy, insecure images containing compilers, headers, and root execution privileges. This post demonstrates how to build a hardened,
When containerizing Python web applications for production, developers often produce heavy, insecure images containing compilers, headers, and root execution privileges.
This post demonstrates how to build a hardened, multi-stage Docker container utilizing a non-root runtime environment, health check verification, and OCI image metadata.
The Production Dockerfile
This multi-stage Dockerfile uses python:3.11-slim-bookworm to separate building dependencies from runtime execution:
# ==============================================================================
# Stage 1: Build & Dependency Packaging
# ==============================================================================
FROM python:3.11-slim-bookworm AS builder
WORKDIR /build
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# ==============================================================================
# Stage 2: Production Runtime
# ==============================================================================
FROM python:3.11-slim-bookworm AS runtime
# OCI Standard Metadata Labels
LABEL org.opencontainers.image.title="Secure API Platform"
LABEL org.opencontainers.image.source="https://github.com/Borino88/secure-api-platform"
LABEL org.opencontainers.image.version="1.0.0"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.authors="Mahdi Fattahi <[email protected]>"
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PATH="/usr/local/lib/python3.11/site-packages:/app:$PATH" \
PYTHONPATH="/app"
RUN groupadd -g 1000 appgroup && \
useradd -u 1000 -g appgroup -s /bin/bash -m appuser
WORKDIR /app
COPY --from=builder /install /usr/local
COPY requirements.txt ./
COPY src/ ./src/
RUN chown -R appuser:appgroup /app
USER appuser
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').getcode() == 200" || exit 1
EXPOSE 8000
CMD ["uvicorn", "src.api.main:app", "--host", "0.0.0.0", "--port", "8000"]
Security Best Practices Highlighted
- Multi-Stage Separation: All compiler tools and cached wheels remain in Stage 1, leaving Stage 2 clean.
-
Non-Root Execution: We define
appuserandappgroupto prevent root execution attacks. - Health Checks: A native, dependency-free Python health check endpoint verification command is integrated directly.
- Vulnerability Mitigation: Hardened bases limit base image CVE counts, validated using Trivy scans.
The configuration templates are available in the public secure-api-platform repository.
Disclosure: This article was prepared with AI-assisted editing and research support. I reviewed the technical content, tested the code and take responsibility for the final article. #ABotWroteThis
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.