Docker & Go image by github.com/ashleymcnamara/gophers

Go — build a minimal docker image in just three steps

Ivan Dlugos
Sep 7 · 3 min read

When you build your Go application for docker, you usually start from some image like golang:1.13. However, it’s a waste of resources to actually use that image for runtime. Let’s take a look at how you can build a Go application as an absolute minimal docker image.


1. Choose a Go version

While it might be tempting to use golang:latest or just golang there are many reasons why this is not such a good idea but the chief one among them is build repeatability. Whether it’s about using the same version for production deployment that you’ve developed and tested on, or if you find yourself in a need to patch an old version of your application, it’s a good idea to keep the Go version pinned to a specific release and only update it when you know it’s going to work with a newer one.

Therefore, always use full specification, including the patch version number and ideally even the base OS that image comes from, e.g. 1.13.0-alpine3.10

2. Keep it minimal

There are two aspects to this — keeping the build time low and keeping the resulting image small.

Fast builds

Docker caches intermediate layers for you so if you structure your Dockerfile right, you can reduce the time it takes for each subsequent rebuild (after a change). The rule of a thumb is to order the commands based on how frequently their source (e.g. source of a COPY) is going to change.

Also, consider using a .dockerignore file which helps keep the build context small — basically, when you run docker build, docker needs to feed everything in the current directory to the build daemon (that Sending build context to Docker daemon message you see at the beginning of a docker build). In short, if your repo contains a lot of data not necessary for building your app (such as tests, markdown for docs generator, etc), .dockerignore will help to speed the build up. At the very least, you can start with the following contents. Dockerfile is there so that if you COPY . . (which you shouldn’t, BTW) doesn’t have to execute and invalidate everything bellow when you change just that Dockerfile.

.git
Dockerfile
testdata

Small images

Very simple — use scratch. Nothing else comes close (because it can’t). Scratch is a special “base” image in that it’s not really an actual image but a completely empty system. Note: in an older version of docker, an explicit scratch image was actually used as a layer, this is no longer the case as of docker 1.5.

How this works is that you use a two-step build inside a single Dockerfile, where you actually build your app on one image, called builder (as an example, it can be actually any name you fancy), then copy the resulting binaries (and all other required files) to a final image based on scratch.

3. Putting it all together

Let’s see how a complete Dockerfile looks like, shall we?

Please leave a comment if you find this useful and/or you would like to share a few tips or tricks of your own.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade