DEV Community

Cover image for Mastering Docker Image Optimization: 6 Strategies for building Lighter, Faster, and Safer images
jlerocher
jlerocher

Posted on • Edited on

Mastering Docker Image Optimization: 6 Strategies for building Lighter, Faster, and Safer images

In containerization's universe, Docker has been the standard for building images for many years. However, as developed applications become more complex, their size also increases, leading to:

  • Extended build times,
  • Higher storage costs,
  • Slower deployments.

To fix these issues, developers or deployment engineers must master the art of optimizing Docker images. In this article, we'll explore 6 strategies for creating lighter and more efficient Docker images.

1. Choose a Minimal Base Image

The fundamental principle of Docker image optimization lies in selecting a lightweight base image. By starting with a minimal base, such as Alpine Linux, you can significantly reduce the size of your Docker images. These lightweight base images contain only the essential components necessary to run applications, thereby eliminating any unnecessary overhead.

Image depicting the lightweight nature of Alpine Linux. It weighs 5 mb

Can you believe it? This image is smaller than 6 MB. 😲

2. Reduce Layers

Docker images are built using a layered file system, with each instruction in the Dockerfile creating a new layer. To optimize the size of the image, you should reduce the number of layers by combining related commands using the && operator. Additionally, cleaning up unnecessary files and dependencies within the same RUN instruction will help streamline the image build process.



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


Enter fullscreen mode Exit fullscreen mode

In this Dockerfile exemple, we use a single RUN command to update packages, install Curl, clean the Apt cache, and remove downloaded package lists.

Bonus

Dive is an open-source tool that allows you to explore the various layers of a Docker image. It shows you the content of each layer and helps you identify voluminous or unnecessary parts.

3. Use Multi-Stage Builds

This process involves dividing the construction of a Docker image into multiple distinct stages. Each stage creates an image layer, but only the final layers are included in the final image. This allows for separating build dependencies (such as compilers, build tools, etc.) from runtime dependencies (such as libraries, binaries, etc.). By doing so, one can benefit from:

  • Reduced image size: By removing unnecessary build artifacts, the final image is lighter.
  • Improved performance: Fewer layers mean faster startup, more efficient resource usage, and better responsiveness.

Example: Node.js Web app with npm



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

   # Stage 2: Final Image
   FROM nginx:alpine
   COPY --from=build /app/dist /usr/share/nginx/html
   EXPOSE 80
   CMD ["nginx", "-g", "daemon off;"]


Enter fullscreen mode Exit fullscreen mode

In this example, we first build the Node.js application, then copy the built files into a lighter Nginx image.

This approach not only reduces the image size but also dramatically improves your CI/CD pipeline's performance.

4. Copy Only Necessary Files: .dockerignore

When adding files to the Docker image, it's essential to include only what is necessary for the application to function. This may seem trivial, but it's important to mention. Use the .dockerignore file for this purpose.
The .dockerignore file excludes unnecessary files and directories from Docker image creation. By defining explicit rules for what to exclude, you can reduce the size of the build context and speed up the build process. This simple yet effective technique streamlines Docker image creation and management. Therefore, avoid copying unnecessary files, directories, or build artifacts that are not necessary for the app to run. By minimizing superfluous content, you save space, and your application is less vulnerable because less code means fewer security vulnerabilities.

For example, among the directories of a nodeJS project that do not belong in a docker image, we have:
node_modules .npm npm-cache logs tmp
The above list is not exhaustive, the only question to ask yourself at this stage is Is this file (or folder) really essential for my application?

5. Minimize Cache Busting

Cache busting can have a significant impact on Docker image build times and efficiency. To optimize caching, you should place instructions that frequently change towards the end of the Dockerfile, ensuring that cached layers are reused as much as possible. This approach reduces build times and improves overall productivity.

6. Inspect Image Size

Regularly monitoring Docker image sizes is essential for identifying areas to optimize and tracking improvements over time. You should use Docker commands such as docker image ls and docker inspect to inspect image sizes and analyze their composition. By remaining vigilant and proactive, developers can continuously optimize Docker images for maximum efficiency.

Optimizing Docker images is a crucial aspect of modern application development, allowing developers to create lightweight, performant, and secure containers. By following key strategies such as choosing minimal base images, reducing layers, and using multi-stage builds, developers can significantly improve build times, reduce storage costs, and streamline deployment processes. It is important to remember that optimization is an ongoing process; therefore, it's essential to regularly revisit your Docker images to identify any new optimization opportunities as your application evolves.

If you find these recommendations helpful, please list in the comments both your initial Docker image size and the final, optimized image size.

Top comments (3)

Collapse
 
mohamad_el_bohsaly profile image
Mohamad El Bohsaly • Edited

Do you have an example of a multi-staged Laravel image?

Collapse
 
jlerocher profile image
jlerocher • Edited

Hi mohamad, for laravel, you can read these excellents posts about laravel multi-staged docker images:
👉 dev.to/er_dward/dockerfile-optimiz...
👉 laravel-news.com/multi-stage-docke...

I hope that these ressources will help you to find the right way to optimize your images👍

Collapse
 
jlerocher profile image
jlerocher

For testing, I utilized a sample Express app. Initially, my Docker image was 1.2 GB, but after applying these tips, its size was reduced to 47 MB.