Why was my container 5x bigger than it needed to be?

March 19, 2025

GODOCKERFILE
Rocket launching
VIEWS: 30COMMENTS: 0

For production environments, when we’re talking about docker images, the smaller the better, it’s faster to upload the image, to download it, and its safer, the smaller the image, the smaller the chances of having a security vulnerability loose in unused code.

The idea behind multi-stage building is to do your docker image build in two or more steps, those next steps can pick up the state of the image and do more work on it.

Before it, keeping the size of images down would require you to manually clean up resources from the image, it was a common practice to have one Dockerfile for development and another one to use for production. The development version contained everything needed to build your application and the production version only contained your application and the dependencies needed to run it.

To optimize our image using multi-stage building let’s start by creating a simple go file called “hello-world.go”:

Go

Next, let’s create our Dockerfile and add:

First, we get a golang image and rename this stage, by default, the stages aren’t named, and you refer to them by their integer number, starting with 0 for the first instruction. However, you can name your stages, by adding an to the instruction. Later on, you can reference this stage by its name. Note that this stage is called “BUILDER”.

Now let’s add the following line:

Go

The "RUN" instruction will execute any commands in a new layer on top of the current image and commit the results. In this case, it creates a folder called “build ”in the container.

Let’s move our hello-world.go to our container using:

Go

The "ADD" instruction copies new files, directories or remote file URLs from and adds them to the filesystem of the image at the path.

Go

The "WORKDIR" instruction sets the working directory for any , , , and instructions that follow it in the .

Go
Dockerfile

This run command creates our go module.

To the next step:

Go

This line is buiding our Go binary, makes it a staticaly-linked binary, running without external dependencies, and the GOOS=linuxindicates that the binary will be compiled for linux.

To the stage 2 we go:

Dockerfile

With multi-stage builds, you use multiple statements in your Dockerfile. Each instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image.

However, the command "From scratch" works a little bit different and it’s useful in the context of building base images (such as debian) or super minimal images (that contain only a single binary and whatever it requires, such as "hello-world"), it will not create an extra layer in your image.

Dockerfile

Next we set our WORKDIR, If the WORKDIR doesn’t exist, it will be created even if it’s not used in any subsequent Dockerfile instruction.

The final step:

Dockerfile

We copy the folder hello-world (created in the previous stage using the command) using the flag “ — from=” and referencing the name we gave our stage (BUILDER) to the following path and set the entrypoint for our container.

The result is an extremely light image:

Container image final size

Container image final size

When the average golang images on dockerhub can reach more than 100MB!

In conclusion, utilizing Docker’s MultiStage building technique offers significant advantages in optimizing the size of your Docker images. Implementing these optimization strategies will enhance your Docker workflows and contribute to a more efficient and scalable software development process.

DISCUSSION.LOG

No comments yet. Be the first to comment!

ADD_COMMENT

© 2025 BITWISEOPS BLOG - All rights reserved