Docker Image Optimization


Docker Image Optimization



Optimizing a Docker image involves several best practices aimed at reducing the image size, improving build times, and ensuring the image runs efficiently in production. Here are a few best practices that can help to shrink docker image size.


  • Minimal Base Image

    Base image plays an important role for your container, It is more like a blueprint of the file system and dependencies or a specific set of directories that are required to run your application with basic configuration in a container environment.

    It has been observed that a base image is the main part that shrinks the image size, therefore, it is recommended to use a lightweight base image like alpine which is only about 5 MB in size.


    Let us understand this with an example.

    Dockerfile with large size

    FROM node:latest
    WORKDIR /usr/src/app
    COPY . .
    RUN npm install
    EXPOSE 3000
    CMD ["node", "app.js"]
    


    The size of the image is 1.14 GB as shown below when you build an image from the above Dockerfile.

    REPOSITORY         TAG       IMAGE ID       CREATED         SIZE
    my_app             latest    00d8fe01d46e   5 minutes ago   1.14GB
    


    Dockerfile with shrink size

    Let us optimize the above docker file using a minimal base image (alpine).

    FROM alpine:latest
    WORKDIR /usr/src/app
    COPY . .
    RUN apk add --no-cache nodejs npm
    RUN npm install
    EXPOSE 3000
    CMD ["node", "app.js"]
    


    The size of the image shrinks 1.14 GB to 103 MB as shown below when you build an image from the above Dockerfile.

    REPOSITORY          TAG       IMAGE ID       CREATED          SIZE
    my_app_shrink       latest    39ed45d97204   20 seconds ago   103MB
    


  • Multi-Stage Build

    Multi-stage builds are a powerful feature in Docker that allows you to use multiple FROM statements in your Dockerfile. This enables you to separate the build environment from the runtime environment, resulting in smaller and more secure images.

    Let us change our original dockerfile into a multi-stage build

    # Stage 1
    FROM node:latest as build
    WORKDIR /app
    COPY . .
    RUN npm install
    # Build the application
    RUN npm run build
    
    # Stage 2: Production Stage
    FROM node:16-alpine
    WORKDIR /app
    COPY --from=build /app/build ./build
    COPY --from=build /app/package*.json ./
    RUN npm install --production
    EXPOSE 80
    CMD ["node", "build/app.js"]
    

    The above code snippet is divided into two stages, Stage 1 is to build, compress the application, and put it into the build folder inside the app. Stage 2 will copy the build folders and other dependencies in the production image.


  • Minimize Layers

    Docker image is composed of a series of layers on top of each other. Each dockerfile command (FROM, RUN, COPY, etc.) adds a new layer to the image. unchanged layers are cached by docker to boost the image build process and efficient use of resources. Each layer increases image size, therefore, it is quite obvious to minimize layers as much as you can, Here are a few tips to minimize layers.

    • Unchanged layers should be placed on top of the image, for example installing system packages or dependencies.

    • Combine RUN instruction as much as possible.

    • Frequently changes should be placed on the bottom of the image.


    For Example: Let us re-write the above docker file by minimizing layers.


    FROM alpine:latest
    WORKDIR /usr/src/app
    COPY . .
    RUN apk add --no-cache nodejs npm && npm install
    EXPOSE 3000
    CMD ["node", "app.js"]
    


  • Optimize Cache Usage

    Unchanged layers are cached by the docker when you are building images so orders matter, Arrange commands that change less frequently at the top of the Dockerfile to take advantage of Docker's layer caching mechanism.


  • Remove Unnecessary Files

    Delete unnecessary files and package caches to reduce the image size.

    FROM alpine:latest
    WORKDIR /usr/src/app
    COPY . .
    RUN apk add --no-cache nodejs npm && npm install
    RUN rm -rf /var/cache/apk/* /tmp/* /var/tmp/*
    EXPOSE 3000
    CMD ["node", "app.js"]
    


  • Include .dockerignore File

    Create a .dockerignore file to exclude files and directories that are not needed in the Docker image.

    .git
    node_modules
    tmp