Table of Contents
Docker is easy to start with and easy to misuse. Below is a list of the most common Docker anti-patterns and how to fix them correctly.
1. Using the ‘latest’ Tag
Bad:
FROM node:latestProblem:
- Non-reproducible builds
- Unexpected breakages after rebuilds
Good:
FROM node:25.5.0-alpine3.22Always pin exact versions. Upgrade intentionally.
2. Bloated Base Images
Bad:
FROM ubuntu:22.04Problem:
- Unnecessary packages
- Large image size
- Bigger attack surface
Good:
FROM node:25-alpine3.22Use the lightweight -alpine images whenever possible.
3. Killing the Build Cache
Bad:
COPY . .RUN npm installProblem:
- Dependencies reinstalled on change any file
Good:
COPY package*.json ./RUN npm ciCOPY . .Order Dockerfile instructions by change frequency.
4. Running as Root
Bad:
WORKDIR /appCOPY . .CMD npm run devProblem:
- Compromised app can run with root privileges
Good:
ARG APP_USER_ID=10001ARG APP_GROUP_ID=10001WORKDIR /appCOPY --chown=${APP_USER_ID}:${APP_GROUP_ID} . .USER ${APP_USER_ID}:${APP_GROUP_ID}CMD npm run devAlways drop root unless absolutely required.
5. Hardcoding Secrets
Bad:
ENV SECRET=...Problem:
- Secrets permanently stored in image layers
Good:
- Pass secrets at runtime
- Use Docker secrets
Example:
docker run -e SECRET=$SECRET myappSecrets must never end up in images.
6. Missing .dockerignore
Bad:
No .dockerignore file
Problem:
- Large build contexts
- Slower builds
Good example:
.gitnode_modulesExclude everything not needed at runtime.
7. No Health Checks
Problem:
- Running container does not mean healthy application
Good:
HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f http://localhost/ || exit 1Health checks allow orchestrators to detect failures.
8. One Image for Development and Production
Bad:
- Debug tools shipped to production
Problem:
- Larger images
- Increased attack surface
Good:
- Use multi-stage builds
- Separate development, build, and production stages
Example:
FROM node:25-alpine3.22 AS builderWORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .# ...
FROM node:25-alpine3.22 AS developmentWORKDIR /appCOPY --from=builder /app ./# ... some dev tools ...
FROM node:25-alpine3.22 AS productionWORKDIR /appCOPY --from=builder /app ./# ... some production tools ...Build for environment (stage):
docker build -t myapp:dev --target development .Build only the required target.
9. Inefficient Layer Usage
Bad:
RUN apt-get updateRUN apt-get install -y curlRUN apt-get install -y wgetRUN apt-get cleanProblem:
- Inefficient because each
RUNcreates a new layer
Good:
RUN apt-get update && \ apt-get install -y curl && \ apt-get install -y wget && \ apt-get cleanCombine related commands into a single layer.
10. Never Updating Base Images
Problem:
- Outdated images contain known CVEs
Good:
docker scout cves myapp:latestScan regularly and update base images on a schedule.