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 <a.borino88@gmail.com>"
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
Top comments (0)