How to Dockerize a GO Application

Leonardo Rodrigues Martins
3 min readOct 10, 2022

--

Gopher Sketch. Image from gophers repository

With Golang, it is possible to build anything from a simple executable tool to full web servers. In order to deliver such applications, Docker is the go-to tool that allows us to create self-contained environments with everything needed for the project to run. It is worth mentioning that the Docker command line interface itself has been developed in GO.

Writing a Docker Image for any GO app

It is generally a good idea to start with a base image that is as minimal as possible and has the basic dependencies needed. The alpine images are normally a solid choice since they contain only the bare minimum that an operating system would need.

So, we can start our Dockerfile with:

FROM golang:alpine3.15

Inside the image, we can specify the next steps where we build the application as we would in any other environment.

FROM golang:alpine3.15WORKDIR /pathCOPY . .RUN go mod download
RUN go build -o "app" .

Then, we must add the execution permission to the generated binary so that it can be run:

RUN chmod +x /path/app

However, there is a small issue running a GO application with such an image. As it turns out, after building and generating the binary file. We no longer need to have GO installed in the container to run it. It is a dependency that could be left aside to optimize the image size. The solution for this problem is to use Docker multi-stage builds.

Using Multi Stage Builds

Multi-Stage builds allow the division of the process of building an image into separate parts and gathering different artifacts from them. A common pattern is known as the builder pattern. Where we start the Dockerfile with the builder phase. It is responsible for assembling the application using its build-time dependencies, then we take only the strictly necessary artifacts from it.

In the Dockerfile above, we have specified the builder image steps. It will build the binary that will be used to run the application. Afterwards, it is going to be transferred to the final image. Notice that the final image is just an ordinary alpine one, there is no need to include any GO dependency since the app has already been built.

Running as non-root

To finish our image, we should run the container as a dedicated user. By default, the processes inside a docker container will run as root. Instead, it is considered a security best practice to run our container as a non-root user. In the case where someone gains access to the container, if some process breaks out of it, it won’t have any privileged permission in the underlying operating system.

Therefore, we can add the following lines to our Dockerfile:

RUN addgroup app_user && adduser -S app_user -u 1000 -G app_userUSER app_user

The Final Result

After following all these steps, we will end up with the following Dockerfile :

As a result, this is a generic image that can be used to ship an application written in GO.

Conclusion

In this post, we have seen how to create a container image of a GO application. Some of the best practices that are related to it were:

  • Using base images with the bare minimum that is needed to run a program.
  • Multi-stage builds as a way of optimizing the image size.
  • Running the container as a non-root user for security reasons.

Thanks for making it to the end. I hope these tips will be useful to you!

Also, make sure to check out my other posts about how to dockerize applications:

--

--

Leonardo Rodrigues Martins

Software developer at Klever | Eager to share my experience and to learn more about technology.