Dockerfile Anti-Patterns: What Not to Do
Avoid Common Pitfalls to Build Better, Faster, and More Secure Docker Images

Search for a command to run...
Avoid Common Pitfalls to Build Better, Faster, and More Secure Docker Images

No comments yet. Be the first to comment.
In this series, I’ll explore Docker best practices, from reducing image sizes to leveraging layer caching and multi-stage builds. Each post provides tips to your workflows, speed up builds, and optimize deployments for efficiency and scalability.
#docker #container #devops #ci/cd
If you've only ever touched Kubernetes through a managed service like GKE, EKS, or AKS, there's a particular kind of clarity that comes from building a cluster with your own hands — one command at a t

Discovering the Kernel, User Space, and Server Multitasking Realities

When you type google.com in your browser, it feels instant. But behind the scenes, there’s a powerful system mapping human-readable domains to machine-friendly IPs: DNS (Domain Name System). For DevOps engineers, DNS is more than just theory—it’s at ...

Introduction In our previous discussion on Docker image optimization, we focused on reducing image size to achieve faster deployments and lower storage costs. Now, let’s address another vital aspect of Docker workflows: build speed. The time it takes...

Docker is an essential tool for modern software development, offering efficiency and scalability in building, shipping, and running applications. However, writing an efficient and maintainable Dockerfile is not always straightforward. Many developers, especially beginners, unknowingly introduce anti-patterns that can lead to bloated images, slow builds, or even security vulnerabilities.
In this blog, we’ll explore some common Dockerfile anti-patterns, their consequences, and best practices to avoid them.
Anti-Pattern:
Choosing a heavy, general-purpose base image, such as ubuntu:latest or debian:latest, for simple applications.
Why It’s a Problem:
Large base images increase the size of your Docker image unnecessarily, leading to longer build and deployment times.
Better Practice:
Use minimal base images, such as alpine, whenever possible. For example, instead of:
FROM ubuntu:latest
Use:
FROM alpine:latest
This drastically reduces the size of the image, often by hundreds of megabytes.
Anti-Pattern:
Including build tools, dependencies, and artifacts in the final image.
Why It’s a Problem:
This makes the image unnecessarily large and exposes tools and files that aren’t required in production.
Better Practice:
Use multi-stage builds to separate build-time dependencies from the final runtime image. For example:
# Stage 1: Build
FROM golang:1.20 as builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# Stage 2: Runtime
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]
This approach ensures that only the necessary runtime files are included in the final image.
Anti-Pattern:
Pulling a base image with the latest tag.
FROM node:latest
Why It’s a Problem:
The latest tag can lead to inconsistent builds if the image updates. This unpredictability can cause issues in production.
Better Practice:
Specify a fixed version tag for consistency and reproducibility. For example:
FROM node:18
This ensures your build uses the same version every time.
Anti-Pattern:
Splitting every command into a separate layer.
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
Why It’s a Problem:
Each RUN instruction creates a new layer, increasing the image size and making it harder to maintain.
Better Practice:
Combine related commands into a single RUN instruction.
RUN apt-get update && apt-get install -y \
curl \
vim
Anti-Pattern:
Failing to exclude unnecessary files from the build context.
Why It’s a Problem:
If your build context contains unnecessary files, such as .git directories or large media files, the build process slows down significantly.
Better Practice:
Create a .dockerignore file to exclude unnecessary files and directories. For example:
.git
node_modules
*.log
This reduces the build context size, speeding up builds.
Anti-Pattern:
Embedding sensitive information, such as API keys or database credentials, in your Dockerfile.
ENV API_KEY=supersecretkey
Why It’s a Problem:
Secrets embedded in images can be extracted, posing a significant security risk.
Better Practice:
Use environment variables or secret management tools to handle sensitive data securely. For example, use Docker’s secrets management in swarm or Kubernetes secrets.
Anti-Pattern:
Leaving behind unnecessary files after package installation.
RUN apt-get update && apt-get install -y \
curl \
vim
Why It’s a Problem:
Temporary files from installations increase the size of your image.
Better Practice:
Clean up temporary files after installation.
RUN apt-get update && apt-get install -y \
curl \
vim && \
rm -rf /var/lib/apt/lists/*
Anti-Pattern:
Relying on the default shell behavior instead of defining a specific CMD or ENTRYPOINT.
Why It’s a Problem:
It leads to ambiguity and makes the container harder to use and debug.
Better Practice:
Specify the intended command or entry point for your application.
CMD ["python", "app.py"]
or, if you need a more robust setup:
ENTRYPOINT ["python"]
CMD ["app.py"]
Writing an efficient Dockerfile is both an art and a science. Avoiding these anti-patterns will result in smaller, faster, and more secure Docker images that are easier to maintain and deploy.
Take the time to review your Dockerfiles and incorporate these best practices into your workflow. With consistent improvements, you’ll ensure your Dockerized applications run smoothly and efficiently.