DEV Community

Abhay Singh Kathayat
Abhay Singh Kathayat

Posted on

Streamline Your Docker Images with Multi-Stage Builds

Multi-Stage Builds in Docker: Optimizing Your Docker Images

Multi-stage builds in Docker allow you to create lean and optimized images by using multiple FROM statements in a single Dockerfile. This approach is particularly useful for building applications where you need tools or dependencies during the build process but don’t want them included in the final image.


Why Use Multi-Stage Builds?

  1. Smaller Image Size:

    By separating the build environment from the runtime environment, you only include the necessary artifacts in the final image.

  2. Improved Security:

    Excluding build-time dependencies reduces attack surfaces in your image.

  3. Cleaner Workflow:

    Consolidates multiple build steps into a single Dockerfile, reducing complexity.


How Multi-Stage Builds Work

A multi-stage build uses multiple stages, each defined by a FROM statement. Intermediate stages can compile code, install dependencies, or run tests, while the final stage packages the application.


Example: Node.js Application

Here’s a Dockerfile for a Node.js application using multi-stage builds:

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

# Stage 2: Production
FROM node:16-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm install --only=production
EXPOSE 3000
CMD ["node", "dist/index.js"]
Enter fullscreen mode Exit fullscreen mode

Breaking Down the Example

  1. Build Stage (FROM node:16 AS build):

    • Installs all dependencies.
    • Builds the application using npm run build.
    • Produces the compiled artifacts in the dist folder.
  2. Production Stage (FROM node:16-alpine):

    • Copies the compiled files from the build stage.
    • Installs only production dependencies using --only=production.
    • Creates a lightweight production image.

Another Example: Go Application

For a Go application, you might have:

# Stage 1: Build
FROM golang:1.18 AS build
WORKDIR /app
COPY . .
RUN go build -o myapp

# Stage 2: Production
FROM alpine:latest
WORKDIR /app
COPY --from=build /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
Enter fullscreen mode Exit fullscreen mode

Advantages of Multi-Stage Builds

  1. Reduced Image Size:

    • Intermediate layers (like build tools) are excluded from the final image.
  2. Simplified CI/CD Pipelines:

    • Combine build and deployment steps in a single file.
  3. Improved Performance:

    • Smaller images lead to faster pull and deploy times.
  4. Cleaner Development:

    • Developers can include debugging tools in build stages without affecting production.

Best Practices

  1. Minimize Build Tools in Final Images:

    Use lightweight base images like alpine for production stages.

  2. Use AS for Naming Stages:

    Name each stage to make your Dockerfile more readable and maintainable.

  3. Leverage Caching:

    Reorder commands to optimize Docker layer caching.

  4. Copy Only Needed Artifacts:

    Avoid copying unnecessary files to the final image.


Use Cases for Multi-Stage Builds

  1. Compiling Code:

    Ideal for languages like Go, Java, or C++ where you need a build environment.

  2. Frontend Applications:

    Build React, Angular, or Vue applications in one stage and serve them using Nginx in the final stage.

  3. Testing:

    Run unit tests in one stage and build the final image only if tests pass.


Stay Connected

Follow me for more Docker tips and insights:

Feel free to connect and share your Docker experiences!

Top comments (0)