Skip to content

Docker Multi-stage Build for Go CLI Tools

Docker multi-stage builds are a pattern used to optimize the size of the final container image by separating the compilation environment from the runtime environment.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md] This is particularly beneficial for Go command-line tools, where the final executable can run on a minimal OS image without requiring the full Go compiler toolchain.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]

Workflow

The build process typically involves a chain of three stages to move from source code to a compact runtime image.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]

1. Development Stage

The process often begins with a "dev" stage that defines the version of the Go language used and sets up a working directory.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md] While the source text references this stage, the example provided focuses heavily on the build and runtime stages, implying the dev stage may be used for interactive containerized development or setting the base image.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]

2. Build Stage

The "build" stage is responsible for compiling the application.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md] * Base Image: It starts from a Go-specific image, such as golang:1.15-alpine.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md] * Compilation: Source code is copied into the container, and the go build command is executed to produce a static binary (e.g., named videos).^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]

3. Runtime Stage

The final "runtime" stage creates the lean production image.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md] * Base Image: It uses a minimal base image, such as alpine.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md] * Artifact Copying: Instead of copying source code or build tools, it uses the COPY --from=build directive to extract only the compiled binary from the previous stage.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md] * Dependencies: Necessary configuration files (e.g., videos.json) or entry point scripts are also copied.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]

Example Configuration

The following Dockerfile demonstrates this pattern for a Go CLI tool named videos.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]

FROM golang:1.15-alpine as dev

WORKDIR /work

FROM golang:1.15-alpine as build

WORKDIR /videos
COPY ./videos/* /videos/
RUN go build -o videos


FROM alpine as runtime 
COPY --from=build /videos/videos /usr/local/bin/videos
COPY ./videos/videos.json /
COPY run.sh /
RUN chmod +x /run.sh
ENTRYPOINT [ "./run.sh" ]

Execution Strategy

To properly handle command-line arguments passed to the container, the binary is often executed via a shell script wrapper defined as the entrypoint.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]

A script like run.sh is used to forward arguments ($@) to the Go application:^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]

#!/bin/sh

videos $@

This setup allows interactions like docker run -it videos get --help to function correctly.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]

Sources