Building Tiny Docker Images Containing Go Microservices

This tutorial shows you how to make small Docker images when building Go programs.

Image for post
Image for post

I wrote two tutorials recently demonstrating Docker Compose and Traefik. They both used a small Docker image containing a simple Go REST API that returns a JSON string with the message Hello World.

Let’s take a look at the code:

As you can see, the code doesn’t do much, it listens on a port specified by the APP_PORT environment variable and returns a JSON response when GET requests are made to /.

The code for this post can be downloaded from this GitHub repository.

Even the small amount of code above can create large Docker images depending on how you build them. Let’s make the smallest image possible in the next step.

Create a Dockerfile and add the following contents to it.

FROM golang:1.15.5-buster AS builder
ARG VERSION=dev
WORKDIR /build
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -o demoservice \
-ldflags="-w -X=main.version=${VERSION}" main.go
FROM scratch
COPY --from=builder /build/demoservice /demoservice
ENTRYPOINT [ "/demoservice" ]

This Dockerfile uses a multi-stage build process. The first five lines are used to build the Go program and the last three copy the built program into a small scratch image.

In order for this to work correctly, we are statically compiling the Go program by specifyingCGO_ENABLED=0. You can read more about statically compiling Go programs here: https://www.arp242.net/static-go.html

We’ve also used -w with -ldflags to strip out debug info. This can make the image a little smaller but if you need debug info, remove this flag.

Build Docker image

Run the following to build the image:

docker build -t demoservice:1.0.0 . --build-arg VERSION=1.0.0
Image for post
Image for post

Note: specifying --build-arg VERSION=1.0.0 assigns 1.0.0 to the variable named version in main.go. This is done at build time using the -ldflags option.

We can check it’s size by running:

docker images
Image for post
Image for post

As you can see from the screenshot above. The size of this image is only 5.08 MB. If we had used a Debian slim image instead of scratch, it would have been 74.3 MB.

We’ve saved a lot of space but does the program still work?

Run the following command:

docker run --rm -e APP_PORT=8080 -p 8080:8080 demoservice:1.0.0
Image for post
Image for post

From another Terminal process run:

curl http://localhost:8080 -i
Image for post
Image for post

Yay, the Go program still works.

By using a statically compiled binary and copying it to a scratch image, we managed to save almost 70 MB of disk space creating a Docker image. This can speed up deployment times and save disk space.

Written by

Software Developer and System Administrator. https://tonymackay.net

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store