Dockerfile and Docker Image Best Practices for Python Developers
This article presents a comprehensive set of Dockerfile and Docker image best‑practice recommendations—including multi‑stage builds, command ordering, minimal base images, layer reduction, unprivileged containers, proper use of COPY versus ADD, caching strategies, health checks, image signing, and resource limits—to help Python developers create smaller, more secure, and maintainable container images.
This article shares a collection of best practices for writing Dockerfiles and using Docker, especially aimed at Python developers. It is organized into two main sections: Dockerfile best practices and Docker image best practices.
Dockerfile Best Practices
1. Use Multi‑Stage Builds
Multi‑stage builds allow you to split the Dockerfile into separate stages, keeping only the final stage in the resulting image. This reduces image size and attack surface.
# temporary stage
FROM python:3.9-slim as builder
WORKDIR /app
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
# final stage
FROM python:3.9-slim
WORKDIR /app
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache /wheels/*The final image is much smaller because build‑time dependencies like GCC are omitted.
2. Order Dockerfile Commands Properly
Place commands that change frequently (e.g., copying source code) near the end of the Dockerfile so that earlier layers can be cached.
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY sample.py .3. Use Small Base Images
Prefer minimal base images such as *-slim or Alpine (with caution) to reduce size and improve security.
REPOSITORY TAG IMAGE ID CREATED SIZE
python 3.9.6-alpine f773016f760e 3 days ago 45.1MB
python 3.9.6-slim 907fc13ca8e7 3 days ago 115MB4. Minimize Layer Count
Combine related RUN commands and avoid unnecessary ADD or COPY steps.
RUN apt-get update && apt-get install -y gcc5. Run Containers as Non‑Root
Create a low‑privilege user and switch to it.
RUN addgroup --system app && adduser --system --group app
USER app6. Prefer COPY Over ADD
Use COPY for local files and ADD only when you need its extra features (remote URLs, automatic extraction).
7. Cache Package Downloads on the Host
Mount the pip cache from the host to avoid re‑downloading packages on each rebuild.
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt8. Run a Single Process per Container
One process per container improves scalability, reusability, logging, and debugging.
9. Use Exec (Array) Syntax for CMD/ENTRYPOINT
Array syntax avoids an extra shell layer and ensures proper signal handling.
# array (exec) form
CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "main:app"]
# string (shell) form
CMD "gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app"10. Understand ENTRYPOINT vs CMD
Use ENTRYPOINT for the immutable part of the command and CMD for default arguments that can be overridden.
11. Add HEALTHCHECK
Define a health check to verify that the container’s service is running correctly.
HEALTHCHECK CMD curl --fail http://localhost:8000 || exit 1Docker Image Best Practices
1. Image Versioning
Avoid the latest tag; use descriptive tags such as timestamps, commit hashes, or semantic versions.
2. Do Not Store Secrets in Images
Never embed passwords, keys, or tokens in the image. Inject them at runtime via environment variables, build‑time args, or secret management tools.
3. Use .dockerignore
Exclude unnecessary files (e.g., .git, .env, build artifacts) to reduce context size and prevent accidental leaks.
**/.git
**/.env
**/venv
Dockerfile
README.md4. Lint and Scan Dockerfiles and Images
Tools like Hadolint, Snyk, Trivy, Clair, and Anchore help enforce best practices and detect vulnerabilities.
5. Sign and Verify Images
Enable Docker Content Trust (DCT) to ensure images are signed and have not been tampered with.
6. Set Memory and CPU Limits
Use --memory, --cpus (or Compose resource limits) to prevent a container from exhausting host resources.
docker run --cpus=2 -m 512m nginxConclusion
The 17 best‑practice recommendations covered in this article help you build Dockerfiles and images that are lean, secure, and maintainable, ultimately saving time, money, and reducing operational risk.
DevOps Engineer
DevOps engineer, Pythonista and FOSS contributor. Created cpp-linter, commit-check, etc.; contributed to PyPA.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
