Borino88

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"]

Enter fullscreen mode Exit fullscreen mode

Security Best Practices Highlighted

  1. Multi-Stage Separation: All compiler tools and cached wheels remain in Stage 1, leaving Stage 2 clean.
  2. Non-Root Execution: We define appuser and appgroup to prevent root execution attacks.
  3. Health Checks: A native, dependency-free Python health check endpoint verification command is integrated directly.
  4. 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