Multi-stage Build with Go
How to build the most secure Go app using a multi-stage build with a runtime base image
Use the Minimus Go image to compile your Go project and build the most secure, vulnerability-free application possible.
Go is a compiled language that is not optimized for use as a runtime; we recommend using a multi-stage build. We’ll walk through the process in detail, but here is a quick summary of what we’ll do.
First compile the binary using the Go image, then copy the binary into a minimal runtime base image (static or glibc-dynamic) to significantly reduce the size of the final container. The final container will be hardened, performant, and secure.
Knowing which runtime base image to use depends on how your Go project was compiled:
- If compiled as a static app, use the static Go image.
- If compiled as a dynamically linked app, use the Glibc-Dynamic image.
Static runtime
About static Go applications
Static binaries are self-contained and do not rely on shared system libraries or runtime dependencies on the host system. As such, they are fully portable and avoid compatibility issues with C libraries. If the static Go binary is mounted on a static base image, it will produce a tiny image, that is as minimal as it gets.
In the example below, the flagCGO_ENABLED=0
is added to the Dockerfile to ensure that the compiled binary is statically linked. CGO is a Go tool that enables the creation of Go packages that call C code. When CGO is disabled, the resulting binary is statically linked.
Example
-
Authenticate to the Minimus registry. Learn more
-
In your project directory, save the code below to a Dockerfile:
-
In your project directory, save the code below to a file and name it
hello-minimus.go
.This is a very simple script which prints “Hello from Minimus!” to the terminal. You can use your own script instead.
-
In your project directory, create a
go.mod
file. This file declares the modules and dependencies required by the project. In our case, the module set is only needed for testing purposes, so it’s very simple. -
Your project directory should now contain 3 files:
Dockerfile
hello-minimus.go
go.mod
-
From your project directory run the following command to build the custom image
hello-go
:The period
.
specifies the current directory as the build context. -
Spin up the image just created with this command:
Dynamic runtime
About dynamic Go applications
If your Go app needs to link to database drivers or other C integrations, you can compile a dynamically linked binary by setting the CGO_ENABLED=1
flag. Make sure the required C libraries are available in both your build and runtime environments.
In the example below, we will use the Minimus Glibc-Dynamic image as a runtime base for a simple web application.
Example
-
Authenticate to the Minimus registry. Learn more
-
In a new project directory, save the code below to a new Dockerfile:
-
Download the example file
hello-minimus-web.go
and save it to your project directory. This is a very simple script that creates a webpage with several tabs so you can navigate between them. -
In your project directory, create a
go.mod
file. This file declares the modules and dependencies required by the project. In our case, the module set is only needed for testing purposes, so it’s very simple. -
Your project directory should now contain 3 files:
Dockerfile
hello-minimus-web.go
go.mod
-
From your project directory run the following command to build the image:
The period
.
specifies the current directory as the build context. -
Spin up the image we just created with this command:
-
Open your browser and go to http://localhost:8080/ to see the default welcome page.