Docker makes packaging and deploying applications much easier — but if you aren’t careful, image sizes can balloon over time. Big images cost you more in storage, longer in deployment and CI/CD pipelines, worse in network time, and present larger attack surfaces. So optimizing images is not just “nice to have” — it has technical, economic, and security impacts.
Below are six proven ways to reduce Docker image size, along with technical trade-offs to consider. You can apply them in your projects, CI/CD pipelines, or during architecture reviews.
Why Image Size Matters
- Performance & Speed: Smaller images pull & startup faster. For large-scale systems (e.g. Kubernetes), minutes saved per deploy add up.
- Cost: Less bandwidth, less storage usage (both on registries and on nodes).
- Security & Maintenance: Fewer libraries = fewer vulnerabilities. Smaller attack surface. Easier to scan and audit.
- Reliability: Less chance of failures in transit or during disk exhaustion.
Methods to Reduce Docker Image Size
Here are six methods you can implement now. I also include which ones are more suitable depending on your stack and constraints.
| Method | What to Do | Technical Implementation | Pros / Trade-offs |
|---|---|---|---|
| 1. Use minimal / distroless base images | Choose base images with tiny footprints (Alpine, BusyBox, or distroless images). | e.g. FROM alpine, or gcr.io/distroless/nodejs etc. Remove unnecessary OS utilities. |
Pros: smallest image, lower attack surface. Cons: harder debugging (no shell), might need more build effort to include needed dependencies. Also some minimal images have limited support or compatibility issues. |
| 2. Multistage builds | Separate build & runtime stages. Build dependencies in one stage, copy only what’s needed to runtime image. | In Dockerfile: FROM builder-base AS build → install, compile → COPY --from=build to lean base → final image contains only runtime code. |
Pros: reduces leftover build tools, dev libs, caches. Trade-offs: more complex Dockerfiles; build time may increase slightly; build infrastructure must support multistage. |
| 3. Minimize number of layers | Combine related commands (especially RUN) and avoid many small layers. Use --no-install-recommends / similar flags. |
Instead of separate RUN apt-get install A then RUN install B etc, combine: RUN apt-get update && apt-get install … && clean …. Clean temp caches in same layer. |
Pros: smaller image, faster builds. Trade-offs: errors harder to debug; less modular build; more verbose single commands. |
| 4. Optimize Dockerfile order & caching | Place dependency installation, OS updates, etc. before copying application code; keep code changes lower in layers. That way Docker’s layer cache helps avoid rebuilding unchanged layers. | For example, move COPY . . after RUN apt-get … and ‘npm install’ etc. |
Pros: faster CI builds; less repeated work. Trade-offs: you must maintain discipline; small code changes still invalidate later layers; caching sometimes leads to stale content if not managed well. |
5. Use .dockerignore (ignore unnecessary files) |
Exclude local files/folders not needed in the image (docs, tests, local configs, git dirs etc). | Create .dockerignore alongside Dockerfile. Example entries: .git, node_modules (if built inside), logs, temp files. |
Pros: smaller build context, faster builds, less chance of leakage of sensitive files. Trade-offs: you might accidentally ignore needed files; need good review. |
| 6. Keep application data outside of the image | Don’t bake runtime data/config/database/file uploads into the image. Use volumes, external storage. | Use Docker volumes or bind mounts; with Kubernetes use PVC (Persistent Volume Claims); keep configs or env vars external (e.g. secrets, mounted config files). | Pros: images remain immutable, stateless; easier portability; smaller base size. Trade-offs: more moving parts; require management of state & backups; mounting volumes adds complexity. |
Additional Tools & Practices
To make these methods more manageable and to enforce them across your teams:
- Dive — explore what’s inside your image, which layers are big, where inefficiencies lie.
- Docker-Slim / SlimToolKit — tools that help you strip out unnecessary bits. Sometimes huge reductions (orders of magnitude) are possible.
- Squashing layers — merging layers to reduce overhead. Use with caution (trade-off rebuild cache).
- Vulnerability Scanning (e.g. Trivy) — doesn’t reduce size, but ensures that what remains is secure.
My Take & Suggested Workflow
Here’s a workflow I recommend, based on experience:
- Audit current images — identify biggest images, which layers or dependencies contribute most size.
- Baseline vs target — set size goals (e.g. “under 200 MB for this service”, or reduce by 50 % from current).
- Apply minimal base + multistage first — biggest wins often here.
- Use
.dockerignoreand reorder Dockerfile next. - Automate checks in CI — e.g. ensure builds fail if image > threshold, run scanning.
- Monitor after deployment — check pull times, storage usage. Adjust if performance regressions.
Example .Dockerfile multistage build
# Stage 1: Build
FROM node:20-alpine AS build
WORKDIR /app
# Install only dependencies first for better caching
COPY package*.json ./
RUN npm ci --only=production
# Copy source code
COPY . .
# Optional: build step (for React/Angular/TypeScript)
# RUN npm run build
# Stage 2: Runtime (slim image)
FROM node:20-alpine AS runtime
WORKDIR /app
# Copy node_modules & code from build stage
COPY --from=build /app /app
EXPOSE 3000
CMD ["node", "server.js"]
.dockerignore example
# Ignore VCS
.git
.gitignore
# Node.js
node_modules
npm-debug.log
yarn-error.log
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
*.pytest_cache
.venv
.env
# Docker
Dockerfile
docker-compose*.yml
.dockerignore
# IDE / Editor
.vscode
.idea
*.swp
Conclusion
Reducing Docker image size isn’t just about bragging rights — it has concrete benefits: faster builds, less bandwidth, lower costs, reduced risk. While some techniques add complexity, the gains in performance & security often justify the effort.
If you begin implementing a few of these in your next project, you’ll likely see 20-80% size reductions, depending on how unoptimized you are now.
Related: Docker Build Cache Optimization: Cutting CI/CD Pipeline Latency by 80%.
Related: Why I Ditched Docker Compose and Went Bare-Metal for My ERP Staging.
Discover more from Susiloharjo
Subscribe to get the latest posts sent to your email.