How to Reduce Docker Image Size?
Reducing Docker image size is an important production optimization technique in DevOps, Kubernetes, microservices, CI/CD pipelines, and cloud-native deployments. Smaller Docker images are faster to build, faster to push, faster to pull, easier to scan, cheaper to store, and safer to run in production.
Why Docker Image Size Matters
In production systems, image size directly impacts deployment speed and cloud cost. When Kubernetes or Docker pulls an image from a registry, a larger image takes more time and bandwidth.
Large Image Problem:
Large Image
|
v
Slow Push to Registry
|
v
Slow Pull in Kubernetes Node
|
v
Slow Pod Startup
|
v
Slow Scaling During Traffic Spike
This becomes critical during high-traffic events like Black Friday in the USA, Boxing Day sales in the UK, or Diwali traffic in India.
Real Production Example
Assume a payment microservice image size is 1.2 GB.
Normal Traffic:
Payment Service -> 2 containers
Heavy Traffic:
Payment Service -> 20 containers
If image size is 1.2 GB:
20 pods may take longer to start
If image size is 250 MB:
20 pods start much faster
Smaller images improve auto-scaling speed and reduce deployment risk.
Best Ways to Reduce Docker Image Size
- Use multi-stage builds
- Use lightweight base images
- Use .dockerignore
- Do not copy unnecessary files
- Use JRE instead of JDK for Java runtime
- Remove package manager cache
- Combine RUN commands carefully
- Avoid installing unnecessary tools
- Use dependency caching properly
- Use distroless images for high-security production
1. Use Multi-Stage Docker Builds
Multi-stage build is the most effective way to reduce Docker image size. It separates the build environment from the runtime environment.
Bad Dockerfile Without Multi-Stage Build
FROM maven:3.9.6-eclipse-temurin-17
WORKDIR /app
COPY . .
RUN mvn clean package -DskipTests
EXPOSE 8080
CMD ["java", "-jar", "target/app.jar"]
This final image contains Maven, source code, build cache, test files, and the JDK.
Good Dockerfile With Multi-Stage Build
FROM maven:3.9.6-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
The final image contains only Java runtime and the application JAR.
2. Use Lightweight Base Images
Base image selection has a huge impact on final image size.
Heavy Base Image
FROM ubuntu:22.04
Better Java Runtime Image
FROM eclipse-temurin:17-jre-jammy
Even Smaller Option
FROM eclipse-temurin:17-jre-alpine
Alpine images are smaller, but always test compatibility because some libraries behave differently with Alpineβs musl libc compared to glibc-based images.
3. Use JRE Instead of JDK in Runtime
For Java Spring Boot applications, the final production image usually does not need Maven or full JDK.
Build Stage:
Maven + JDK
Runtime Stage:
JRE only
This significantly reduces image size and attack surface.
4. Use .dockerignore
A .dockerignore file prevents unnecessary files from being sent to
Docker build context.
Recommended .dockerignore
.git
.gitignore
.idea
.vscode
target
logs
*.log
*.tmp
.env
node_modules
README.md
Dockerfile
docker-compose.yml
*.sql
Without .dockerignore, Docker may copy unnecessary files into the build context, increasing build time and sometimes image size.
5. Do Not Copy the Entire Project Blindly
Bad Example
COPY . .
This can copy logs, local configs, SQL dumps, temporary files, Git history, and IDE files.
Better Example
COPY pom.xml .
COPY src ./src
Copy only what is required to build the application.
6. Remove Package Manager Cache
If you install packages, remove package cache in the same layer.
Bad Example
RUN apt-get update
RUN apt-get install -y curl
Good Example
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
This prevents package lists and cache files from remaining in the final image.
7. Avoid Unnecessary Tools in Runtime Image
Production runtime images should not contain tools that are only useful for debugging or building.
Avoid in runtime image:
- Maven
- Gradle
- Git
- curl unless required
- wget unless required
- build-essential
- compilers
- test tools
Keep the runtime image minimal.
8. Combine RUN Commands Carefully
Docker images are layer-based. Each RUN instruction creates a new layer.
Bad Example
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
Good Example
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
Cleaning must happen in the same RUN layer to reduce final image size.
9. Use Distroless Images
Distroless images contain only the application and runtime dependencies. They do not include shell, package manager, or unnecessary OS tools.
Example
FROM gcr.io/distroless/java17-debian12
COPY app.jar /app/app.jar
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Distroless images are smaller and more secure, but debugging is harder because they do not include shell tools.
10. Use Spring Boot Layered JARs
Spring Boot supports layered JARs, which can improve Docker layer caching.
java -Djarmode=layertools -jar app.jar extract
This separates dependencies, snapshot dependencies, resources, and application classes into different layers.
Spring Boot Layered Dockerfile Example
FROM maven:3.9.6-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-jammy AS extract
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
COPY --from=extract /app/dependencies/ ./
COPY --from=extract /app/spring-boot-loader/ ./
COPY --from=extract /app/snapshot-dependencies/ ./
COPY --from=extract /app/application/ ./
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
11. Use Specific Image Tags
Avoid latest because it is unpredictable.
Bad
FROM openjdk:latest
Good
FROM eclipse-temurin:17-jre-jammy
Stable base image versions improve reproducibility and rollback safety.
12. Analyze Image Layers
Use tools to identify which layer is increasing image size.
docker history image-name
docker image inspect image-name
You can also use tools like dive to inspect image layers.
13. Clean Build Artifacts
Avoid keeping build outputs, temporary folders, and package caches inside final runtime images.
Remove:
- /tmp files
- package manager cache
- build cache
- test reports
- source code
- unused dependencies
14. Use External Storage for Files
Do not bake uploads, logs, SQL dumps, or user-generated files into images.
Bad
COPY uploads /app/uploads
COPY logs /app/logs
COPY backup.sql /app/backup.sql
Good
Uploads -> S3 / volume
Logs -> stdout / Loki / ELK / CloudWatch
Database backups -> external backup storage
15. Use Minimal Node.js Build for Frontend
For React, Angular, or Vue applications, build with Node.js but serve with Nginx.
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
The final image does not contain Node.js or source code.
Image Size Reduction Strategy Table
| Technique | Impact |
|---|---|
| Multi-stage build | Very high |
| Lightweight base image | High |
| .dockerignore | High |
| JRE instead of JDK | High |
| Remove package cache | Medium |
| Distroless image | High |
| Layered Spring Boot JAR | Medium to high |
Bad Dockerfile Example
FROM openjdk:latest
WORKDIR /app
COPY . .
RUN apt-get update
RUN apt-get install -y curl git vim
RUN mvn clean package -DskipTests
EXPOSE 8080
CMD ["java", "-jar", "target/app.jar"]
Why This Image Becomes Large
- Uses heavy and unpredictable base image
- Copies entire project
- Installs unnecessary tools
- Does not clean package cache
- Includes build tools in final image
- Does not use multi-stage build
Optimized Production Dockerfile
FROM maven:3.9.6-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
RUN groupadd -r appuser && useradd -r -g appuser appuser
COPY --from=build /app/target/*.jar app.jar
RUN chown -R appuser:appuser /app
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
Production Checklist to Reduce Docker Image Size
[ ] Use multi-stage build
[ ] Use lightweight runtime image
[ ] Use JRE instead of JDK
[ ] Add .dockerignore
[ ] Copy only required files
[ ] Avoid latest tag
[ ] Remove package manager cache
[ ] Avoid unnecessary runtime tools
[ ] Use external storage for uploads/logs
[ ] Analyze image layers
[ ] Scan final image
Interview Answer
Docker image size can be reduced by using multi-stage builds, lightweight base images, .dockerignore, JRE instead of JDK for Java applications, removing package manager cache, avoiding unnecessary tools, and copying only required files.
In production, smaller images improve Kubernetes pod startup time, reduce network transfer, lower cloud storage cost, improve security scanning speed, and make deployments faster and more reliable.
Useful Internal Links
- Docker Interview Questions
- DevOps Interview Questions
- Kubernetes Interview Questions
- Microservices Interview Questions
- AWS Interview Questions
- Spring Boot Interview Questions
Final Conclusion
Reducing Docker image size is a critical production best practice. It improves deployment speed, scaling speed, security, cost efficiency, and CI/CD performance.
The best approach is to use multi-stage builds, lightweight runtime images, .dockerignore, minimal dependencies, proper caching, and external storage for runtime data. These practices make Docker images production-ready for Kubernetes, AWS ECS, Docker Compose, and modern microservices platforms.