"Python Container Security: Docker + Python Best Practices"
How to secure Python Docker containers. Covers base image selection, vulnerability scanning, layer optimization, secrets in containers, and runtime security. Practical Dockerfile examples.
Docker makes deploying Python apps easy. But the default Dockerfile has security holes that most developers don't notice. Base images with 200+ CVEs, running as root, hardcoded secrets in layers — it's all common.
This guide shows you how to build secure Python Docker containers from scratch.
Why Container Security Matters
Containers share the host kernel. A vulnerability in your container image can compromise the entire host. The numbers:
- 67% of container images have at least one critical vulnerability
- Average Python image: 150-300 CVEs in base packages
- Ubuntu base image: 200+ known CVEs before patching
- Alpine base image: 20-50 CVEs (much smaller attack surface)
Choosing the Right Base Image
| Image | Size | CVEs | Best For |
|---|---|---|---|
| python:3.12 | 900MB | 200+ | Development only |
| python:3.12-slim | 130MB | 50+ | Production (recommended) |
| python:3.12-alpine | 50MB | 20+ | Minimal production |
| gcr.io/distroless/python3 | 25MB | 5-10 | Maximum security |
| python:3.12-slim-bookworm | 130MB | 30+ | Production (Debian-based) |
Rule of thumb: Use python:3.12-slim for most production workloads. Use distroless for maximum security.
Secure Dockerfile Template
# Stage 1: Build
FROM python:3.12-slim AS builder
WORKDIR /app
# Install dependencies first (cache layer)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Stage 2: Production
FROM python:3.12-slim
# Security: Don't run as root
RUN useradd --create-home --shell /bin/bash appuser
USER appuser
WORKDIR /app
# Copy only what's needed
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY . .
# Security: Remove unnecessary tools
RUN apt-get purge -y --auto-remove gcc && rm -rf /var/lib/apt/lists/*
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
EXPOSE 8000
CMD ["python", "app.py"]
Key Security Practices
1. Never Run as Root
# WRONG
CMD ["python", "app.py"] # Runs as root
# RIGHT
RUN useradd --create-home appuser
USER appuser
CMD ["python", "app.py"]
2. Use Multi-Stage Builds
# Build stage (has compilers, dev tools)
FROM python:3.12 AS builder
RUN pip install wheel setuptools
COPY requirements.txt .
RUN pip wheel --no-deps -r requirements.txt -w /wheels
# Production stage (minimal)
FROM python:3.12-slim
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir --no-index --find-links=/wheels -r /dev/stdin << EOF
-r requirements.txt
EOF
3. Pin Base Image Versions
# WRONG — uses latest, unpredictable
FROM python:3.12
# RIGHT — pinned to specific version
FROM python:3.12.4-slim-bookworm@sha256:abc123...
4. Scan Your Images
# Scan with VulnLedger
vulnledger scan docker://myapp:latest
# Scan with Trivy (alternative)
trivy image myapp:latest
# Scan in CI/CD
- name: Scan container
run: |
pip install vulnledger
vulnledger scan docker://myapp:${{ github.sha }}
5. Don't Store Secrets in Images
# WRONG — secret baked into image layer
ENV API_KEY=sk-abc123
# RIGHT — use runtime secrets
# Docker Compose:
# environment:
# - API_KEY=${API_KEY}
# Kubernetes:
# env:
# - name: API_KEY
# valueFrom:
# secretKeyRef:
# name: app-secrets
# key: api-key
6. Use Read-Only Filesystem
# In docker-compose.yml
services:
app:
image: myapp:latest
read_only: true
tmpfs:
- /tmp
Container Security Checklist
For every Python Docker image:
- [ ] Base image pinned to specific version
- [ ] Multi-stage build (no compilers in production)
- [ ] Running as non-root user
- [ ] No secrets in image layers
- [ ] Image scanned for CVEs
- [ ] Read-only filesystem (where possible)
- [ ] Health check configured
- [ ] Resource limits set (CPU, memory)
- [ ] No unnecessary packages installed
- [ ] SBOM generated for the image
How VulnLedger Helps
VulnLedger scans Docker images for vulnerabilities:
# Scan a local image
vulnledger scan docker://myapp:latest
# Scan from Dockerfile
vulnledger scan .
# Generate SBOM for the image
vulnledger scan docker://myapp:latest --json --output image-sbom.json
Conclusion
Container security is about reducing attack surface. Use minimal base images, don't run as root, scan for CVEs, and never store secrets in images. These 4 practices eliminate the most common container vulnerabilities.
Previous: Python Web App Security: Flask, FastAPI, Django
Next: Python CI/CD Security: GitHub Actions for Python
See also: Docker Container Scanning Guide | Continuous SBOM Monitoring