Operations 29 min read

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.

DevOps Engineer
DevOps Engineer
DevOps Engineer
Dockerfile and Docker Image Best Practices for Python Developers

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    115MB

4. Minimize Layer Count

Combine related RUN commands and avoid unnecessary ADD or COPY steps.

RUN apt-get update && apt-get install -y gcc

5. Run Containers as Non‑Root

Create a low‑privilege user and switch to it.

RUN addgroup --system app && adduser --system --group app
USER app

6. 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.txt

8. 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 1

Docker 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.md

4. 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 nginx

Conclusion

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.

ContainerizationsecurityDockerfilebest-practices
DevOps Engineer
Written by

DevOps Engineer

DevOps engineer, Pythonista and FOSS contributor. Created cpp-linter, commit-check, etc.; contributed to PyPA.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.