Docker Best Practices: Optimizing Your Builds for Speed and Efficiency

Every day, we build a lot of docker images. You know everything; still, I listed some best practices for optimizing docker build that I follow in day-to-day development. By the way, many best practices are already mentioned on the Docker website. I will share what I do for Golang docker image building. It’s applicable almost all kind of builds.

Always do Multi-Stage docker build. It has a lot of power. It minimizes the size and improves run time performance.


FROM golang:1.23.1-alpine AS builder

# Set the working directory inside the container
WORKDIR /app

# Copy go mod and sum files
COPY go.mod ./

# Download all dependencies
RUN go mod download

# Copy the source code into the container
COPY . .

RUN GOOS=linux go build -o /hello main.go

FROM scratch
# Set the working directory
WORKDIR /

# Copy the binary from the build stage
COPY --from=builder /hello /hello

# Expose the port the app runs on
EXPOSE 8080

# Run the binary
CMD ["/hello"]

 

I always use the alpine of Golang official image with the proper version. A fixed version will prevent breaking changes in future. However, it’s not the end of the world; if a patch exists, docker will automatically resolve it. As of writing this blog post, I used golang:1.23.1-alpine3.20. alpine is the minimum and stable image.

Every command in a Dockerfile (like RUN, COPY, etc.) generates a separate layer in the final image. Group similar type of commands together into one step reduces the total number of layers. E.g.


COPY go.mod ./
COPY go.sum ./

You should do:


COPY go.mod go.sum ./

Another example if you need to install any dev dependencies,


RUN apk update
RUN apk add --no-cache git
RUN rm -rf /var/cache/apk/* *

You should do:


RUN apk update && apk add --no-cache git && rm -rf /var/cache/apk/*

There is a tool called dive, which can analyze the size of every layer of docker builds.

Another point I wanna bring to your attention. I splitted the copy command into two. First, I copied go.mod and go.sum, so that I can download all the GO dependencies in a layer. Then, I copied the rest of the source code COPY . .. It avoids rebuilding those layers when there are changes in the source code.

Instead, the COPY command can be split in two. First, copy over the package management files (in this case, package.json and yarn.lock). Then, install the dependencies. Finally, copy over the project source code, which is subject to frequent change.

By default, docker transfers all the files from the project directory. Use a .dockerignore file to exclude all unnecessary files for image building. For example, my .dockerignore looks like


# The .dockerignore file excludes files from the container build process.

# Exclude locally vendored dependencies.
vendor/

# Exclude "build-time" ignore files.
.dockerignore

'# Exclude others.
.gitignore
.idea

Use COPY always instead of ADD. There is only one exception when you add a local tar file with auto-extraction capability. E.g.


ADD any-local-file.tar.xz /app

Finally, I wanna share an optimization technique and one more tool for scanning vulnerabilities. You can speed up the build by using a persistent cache.


ENV GOCACHE=/root/.cache/go-build
RUN --mount=type=cache,target=/go/pkg/mod/ \
--mount=type=cache,target="/root/.cache/go-build" \
--mount=type=bind,target=. \
go build -o /hello main.go

trivy is a static scanning tool that can scan docker Images for vulnerabilities. You can integrate it into the CI/CD pipeline.

Feel free to add more best practices or optimizations by adding comments. Happy Hacking 🙂

 

Eftakhairul Islam

Hi, I'm Eftakhairul Islam, a passionate Software Engineer, Hacker and Open Source Enthusiast. I enjoy writing about technical things, work in a couple of startup as a technical advisor and in my spare time, I contribute a lot of open source projects.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

 

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Read previous post:
Github’s “repository not found” error and How to fix it

GitHub is an excellent platform for managing code repositories and collaborating with other developers. One of the great features of...

Close