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:

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

  1. Authenticate to the Minimus registry. Learn more

  2. In your project directory, save the code below to a Dockerfile:

    # Pull Go image and set it as builder
    FROM reg.mini.dev/go:latest AS builder
    
    # Set CGO_ENABLED to 0 to create a static binary
    ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
    
    # Copy project directory content to app directory in the container
    COPY . /app
    
    # Compile your Go app
    RUN cd /app && go build -o go-minimus .
    
    # Pull static image to use as a runtime base image
    FROM reg.mini.dev/static:latest
    
    # Copy go-minimus binary from the /app directory in the builder container to /usr/bin/ in the final static container
    COPY --from=builder /app/go-minimus /usr/bin/
    
    # Set the container entrypoint to run the go-minimus binary when the container is started
    ENTRYPOINT ["/usr/bin/go-minimus"]
    
  3. In your project directory, save the code below to a file and name ithello-minimus.go.

    This is a very simple script which prints “Hello from Minimus!” to the terminal. You can use your own script instead.

    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("Hello from Minimus!")
    }
    
  4. 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.

    module minimus.dev/hello_minimus
    
    go 1.19
    
  5. Your project directory should now contain 3 files:

    1. Dockerfile
    2. hello-minimus.go
    3. go.mod
  6. From your project directory run the following command to build the custom image hello-go:

    docker build -t hello-go .
    

    The period . specifies the current directory as the build context.

  7. Spin up the image just created with this command:

    docker run hello-go
    

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

  1. Authenticate to the Minimus registry. Learn more

  2. In a new project directory, save the code below to a new Dockerfile:

    # Pull Go image and set it as builder
    FROM reg.mini.dev/go AS builder
    
    # Set CGO_ENABLED=1 to compile a dynamically linked binary
    ENV CGO_ENABLED=1 GOOS=linux GOARCH=amd64
    
    # Copy the project directory content to app directory in the container
    COPY . /app
    
    # Compile Go application
    RUN cd /app && go build -o Hello-Minimus-Web .
    
    # Pull dynamic image to use as a runtime base
    FROM reg.mini.dev/glibc-dynamic:latest
    
    # Copy Go binary from the /app directory in the builder container to /usr/bin/ in the final dynamic container
    COPY --from=builder /app/Hello-Minimus-Web /usr/bin/
    
    EXPOSE 8080
    
    # Set entrypoint to run the binary when the container is started
    ENTRYPOINT ["/usr/bin/Hello-Minimus-Web"]
    
  3. Download the example filehello-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.

  4. 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.

    module minimus.dev/hello_minimus
    
    go 1.19
    
  5. Your project directory should now contain 3 files:

    1. Dockerfile
    2. hello-minimus-web.go
    3. go.mod
  6. From your project directory run the following command to build the image:

    docker build -t hello-go-web .
    

    The period . specifies the current directory as the build context.

  7. Spin up the image we just created with this command:

    docker run -p 8080:8080 hello-go-web
    
  8. Open your browser and go to http://localhost:8080/ to see the default welcome page.