Dockerfiles look simple — a few lines of commands to install dependencies and copy files. But the difference between a naive Dockerfile and a production-quality one is significant: image size, build time, cache efficiency, and security all depend on how the Dockerfile is written.
A Dockerfile linter analyzes your file against known best practices and reports issues before you spend time building and pushing an image that will fail security scans or download unnecessary gigabytes of data.
Common Dockerfile Mistakes
Using `latest` tag for base images
# Bad FROM node:latest # Good FROM node:20.11.0-alpine3.19
The latest tag resolves to a different image over time. Builds that work today may fail in six months when latest points to a new major version with breaking changes. Pin to a specific version tag for reproducible builds.
Not using `.dockerignore`
Without a .dockerignore file, the COPY . . instruction copies your entire project directory into the image — including node_modules, .git, .env files, and build artifacts. This inflates the image size and may expose secrets.
Create a .dockerignore:
node_modules .git .env *.log coverage/ dist/
Running as root
By default, containers run as root. If a vulnerability in your application is exploited, the attacker has root access inside the container. Create a non-root user:
RUN addgroup --system app && adduser --system --group app USER app
Not combining RUN instructions
Each RUN instruction creates a new image layer. Having many separate RUN instructions increases image size and slows builds:
# Bad — 3 layers RUN apt-get update RUN apt-get install -y curl wget RUN apt-get clean # Good — 1 layer, smaller image RUN apt-get update && apt-get install -y curl wget && apt-get clean && rm -rf /var/lib/apt/lists/*
Not cleaning up package manager caches
Package managers leave caches in the image after installation. Always clean up in the same layer as the install to prevent the cache from being committed to the image.
Ignoring layer cache order
Docker caches layers. If a layer changes, all subsequent layers are invalidated. Copy dependency manifests first, install dependencies, then copy source code. This way, source code changes do not invalidate the dependency installation layer:
# Correct order — dependencies cached separately from source COPY package.json package-lock.json ./ RUN npm ci COPY . .
Missing HEALTHCHECK
A HEALTHCHECK instruction tells Docker how to test that the container is still functioning:
HEALTHCHECK --interval=30s --timeout=10s --retries=3 CMD curl -f http://localhost:3000/health || exit 1
Without this, Docker assumes the container is healthy as long as the process is running, even if the application is deadlocked.
Multi-stage Builds
Multi-stage builds are one of the most powerful Dockerfile optimizations. They let you use a full build environment (with compilers, dev dependencies, test tools) without shipping it in the final image:
# Build stage FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage — only the built output FROM node:20-alpine AS production WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY --from=builder /app/dist ./dist USER node EXPOSE 3000 CMD ["node", "dist/server.js"]
The final image contains only the runtime and the compiled output — no dev dependencies, no build tools, no source code.
Using the DevHexLab Dockerfile Linter
Paste your Dockerfile into the linter. It checks for:
- Pinned base image versions
- Non-root user
apt-get/yumcache cleanup- Layer cache ordering
ADDvsCOPY(preferCOPYunless you needADD's tar extraction)WORKDIRset beforeCOPY/RUNEXPOSEdocumentationCMDvsENTRYPOINTusage- Secrets in
ENVinstructions (flagged as a security risk)
Each finding includes a severity (error, warning, info) and a specific suggestion.
Conclusion
A linted, well-structured Dockerfile produces smaller images, faster builds, and a more secure runtime. The DevHexLab Dockerfile Linter catches the most common mistakes automatically so you can focus on your application rather than container configuration details.