Monday, 30 June 2025

how to optimize docker file

How to Optimize Dockerfile

✅ 1. Use a Minimal Base Image

Before:

FROM ubuntu:20.04

After (optimized):

FROM alpine:3.20

Alpine is only ~5MB. Use it unless you need full OS features.

✅ 2. Use Multi-Stage Builds

Before:

FROM golang:1.21
WORKDIR /app
COPY . .
RUN go build -o myapp
CMD ["./myapp"]

After (optimized):

# Build stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# Runtime stage
FROM alpine:3.20
COPY --from=builder /app/myapp /myapp
CMD ["/myapp"]

Reduces final image size significantly by excluding build tools.

✅ 3. Minimize Layers

Before:

RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean

After (optimized):

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

Fewer layers = smaller image + faster builds.

✅ 4. Use .dockerignore

Add a .dockerignore file:

nginx
node_modules
.git
*.log
Dockerfile
README.md

Prevents unnecessary files from being sent to Docker build context.

✅ 5. Leverage Build Cache

Order Dockerfile instructions from least to most frequently changed.

Good Order:

COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

Reuse cache for dependencies if code changes frequently.

✅ 6. Avoid Running as Root

Add a user:

RUN addgroup app && adduser -S -G app appuser
USER appuser

Improves container security.

✅ 7. Clean Up Temporary Files

Remove caches, build artifacts, and logs:

RUN make build && rm -rf /tmp/*

✅ 8. Pin Versions

Pin base images and packages for reproducibility.

FROM node:18.20.2-alpine
RUN apk add --no-cache curl=8.4.0-r0

✅ 9. Combine COPY and RUN Efficiently

Before:

COPY script1.sh .
COPY script2.sh .
RUN chmod +x script1.sh script2.sh

After:

COPY script*.sh ./
RUN chmod +x script*.sh

✅ 10. Use Distroless or Scratch (Advanced)

Distroless:

FROM gcr.io/distroless/static-debian11
COPY myapp /
CMD ["/myapp"]

No package manager, shell, or utilities — ultra-minimal and secure.

๐Ÿ” Final Tip: Test Your Image Size

After building:

docker images
docker image inspect <image>

Sample Optimized Dockerfile (Go Example)

# Build stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod ./
COPY go.sum ./
RUN go mod download
COPY . .
RUN go build -o server

# Runtime stage
FROM alpine:3.20
RUN adduser -S -G app appuser
WORKDIR /home/appuser
COPY --from=builder /app/server .
USER appuser
EXPOSE 8080
ENTRYPOINT ["./server"]

What is a Multi-Stage Build in Docker?

Multi-stage builds allow you to use multiple FROM statements in a single Dockerfile to separate the build environment from the runtime environment.

✅ Why Use Multi-Stage Builds?

Benefit Explanation
๐Ÿงน Smaller Images Final image contains only what's needed to run the app. No compilers or dev tools.
๐Ÿ”’ More Secure No leftover build artifacts or secrets.
⚡ Faster Deployment Less data to push/pull.
๐Ÿ“ฆ Cleaner Architecture Build logic is separated from runtime logic.

๐Ÿ› ️ Example: Without vs With Multi-Stage Build

❌ Without Multi-Stage (All-in-one)

FROM golang:1.21
WORKDIR /app
COPY . .
RUN go build -o myapp
CMD ["./myapp"]

Problem: Final image contains Go compiler, build cache, and source code.

Image Size: ~1 GB+

✅ With Multi-Stage Build

# Stage 1: Builder
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# Stage 2: Final runtime image
FROM alpine:3.20
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]

Final Image: Only has /myapp binary.
No source code, no compiler, just what's needed.
Image Size: ~20 MB

๐Ÿงฉ Real Use Case: Node.js with Multi-Stage

# Build Stage
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Production Stage
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]

๐Ÿ 1. Python (FastAPI + Uvicorn) – Multi-Stage Dockerfile

Assumes:
App entrypoint: main.py
Dependencies in requirements.txt

# Stage 1: Install dependencies in isolated layer
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --upgrade pip && \
    pip install --user -r requirements.txt

# Stage 2: Production image
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

✅ No dev tools in final image, small size, ready for production.

๐ŸŸฉ 2. Node.js (Express) – Multi-Stage Dockerfile

Assumes:
app.js as entry
Build step not needed (i.e., no TypeScript or Webpack)

# Stage 1: Build dependencies
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install

# Stage 2: Production image
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]

✅ Final image only contains app and modules — not cache or dev dependencies.

☕ 3. Java (Spring Boot) – Multi-Stage Dockerfile

Assumes:
JAR file built via Maven/Gradle
Entry JAR: target/app.jar

# Stage 1: Build app with Maven
FROM maven:3.9.6-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests

# Stage 2: Slim runtime image
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=builder /build/target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

✅ Uses only JRE in runtime stage — reduces size by 60–70% compared to fat images.

๐Ÿ†š RUN vs CMD in Docker

Feature RUN CMD
Purpose Executes at build time Executes at container runtime
Used For Installing packages, configuring files Running the app or setting default command
Effect Creates a new image layer Does not create a layer in the image
Output Changes the image (e.g., files installed) Sets the container’s default execution
Runs when During docker build During docker run
Override Cannot override in docker run Can be overridden in docker run

๐Ÿงช Example

✅ RUN

RUN apt-get update && apt-get install -y curl

This installs curl at image build time. The result is saved in the image.

✅ CMD

CMD ["python", "main.py"]

This runs the app when the container starts (e.g., docker run myimage).

You can override CMD like:

docker run myimage python other_script.py

๐Ÿ”„ Can You Use Both?

Yes — very common.

FROM python:3.11
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "main.py"]

RUN: Install deps once into image.
CMD: Start the app when container runs.

You want to...

Use RUN CMD
Install software, copy files, or prep image RUN
Set the default command to run when container starts CMD

No comments:

Post a Comment