🐳 Building Secure & Lightweight Docker Images with Multi-Stage Builds and Distroless

Chaitanya VamsiChaitanya Vamsi
3 min read

In the world of DevOps, security, efficiency, and performance are non-negotiable when building Docker containers. Large, bloated images increase build times, consume more bandwidth, and pose security risks.

This post walks you through multi-stage builds and the use of Distroless images—two powerful strategies to ship production-grade containers that are tiny, fast, and secure.


🚫 The Problem with Traditional Docker Images

Most base images like ubuntu, debian, or even openjdk come with:

  • Bash or shell

  • System utilities

  • Package managers like apt

While convenient, these additions can:

  • 📦 Bloat image size (200MB+)

  • 🐛 Add unused software and dependencies

  • 🔓 Increase attack surface (e.g., shell access in prod)

Why Does This Matter?

A smaller image:

  • 🏃‍♂️ Deploys faster

  • 🔐 Is more secure

  • ☁️ Saves bandwidth and storage

  • 📦 Reduces supply chain vulnerabilities


✅ What Are Distroless Images?

Distroless images (from Google) are base images that include only your app and its runtime dependencies, nothing else.

🔐 Benefits:

  • No shell, SSH, or package manager

  • Image sizes can be as small as ~2–10MB

  • Harder for attackers to exploit

  • Ideal for production environments

⚠️ You can’t exec into a Distroless container (docker exec -it container bash) — it doesn’t even have a shell. Debug locally or during CI builds instead.


🧱 Enter Multi-Stage Builds

Distroless is great for runtime, but you still need full-featured tools to build your app. That’s where multi-stage builds come in.

You define multiple FROM blocks in a single Dockerfile:

  1. Stage 1: Build the app (using Maven, Node, etc.)

  2. Stage 2: Run the app (using a lightweight or Distroless base)


📦 Example: Java App Dockerfile (Multi-Stage)

Here’s a production-grade Dockerfile for a Java app using Maven:

# Stage 1: Build stage 
FROM maven:3.8.5-openjdk-17-slim AS build
WORKDIR /app

# Copy dependencies first for Docker layer caching
COPY pom.xml mvnw ./
# Then copy all source files
COPY . .

# Build the app and skip tests to speed up build
RUN ./mvnw package -DskipTests

# Stage 2: Runtime stage
FROM openjdk:11-jre-slim
WORKDIR /app

# Copy only the compiled JAR file
COPY --from=build /app/target/*.jar app.jar

# Expose the port your app runs on
EXPOSE 8080

# Start the application
CMD ["java", "-jar", "app.jar"]

Optional Upgrade: Use a Distroless Runtime

Swap the second FROM line with this for better security:

FROM gcr.io/distroless/java17

Make sure your app doesn't require a shell or file system tools at runtime before switching.


📉 Image Size Tips

Here’s how to make your Docker images as lean as possible:

Combine RUN steps:

RUN apt update && apt install -y curl && rm -rf /var/lib/apt/lists/*

Use .dockerignore:
Exclude files like .git, target/, node_modules/, and README.md.

Pin exact versions:
Avoid FROM openjdk:latest. Always pin image versions to avoid surprises.

Copy only what you need:
Don't copy the entire context if all you need is a JAR file.


🚀 Push Image to Docker Hub

Once built, tag and push your image:

# Tag the image
docker tag oldNAmeOfImage userName/NewName:v1

# Push it
docker push userName/NewName:v1

To see container logs:

docker logs ContainerName

🧠 Quick Recap

FeatureTraditional ImageDistroless + Multi-stage
Image Size200–500MB~10–80MB
Shell & SSHYesNo
SecurityMediumHigh
Build Tools IncludedYesNo
Best forDev & DebugCI/CD & Production

🎯 Final Thoughts

Multi-stage builds with Distroless base images are a modern best practice for building Docker images. You get the flexibility of full-featured build environments without compromising on security or performance in production.

This approach is ideal for:

  • 🧩 Microservices

  • ☁️ Cloud-native apps

  • 📦 Kubernetes deployments

  • 🔐 Secure CI/CD pipelines

0
Subscribe to my newsletter

Read articles from Chaitanya Vamsi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Chaitanya Vamsi
Chaitanya Vamsi