Skip to main content

Use Dagger with Multi-stage Container Builds

Multi-stage builds are a common practice when building containers with Docker.

  • First, your application is compiled in a context which has tools that are required for building the application, but not necessarily required for running it.
  • Next, to reduce the number of dependencies and hence the size of the image, the compiled application is copied to a different base image which only has the required components to run the application.

This guide explains how to perform multi-stage builds with the Dagger SDKs.

Requirements

This guide assumes that:

  • You have a Go, Python or Node.js development environment. If not, install Go, Python or Node.js.
  • You have a Dagger SDK installed for one of the above languages. If not, follow the installation instructions for the Dagger Go, Python or Node.js SDK.
  • You have Docker installed and running on the host system. If not, install Docker.
  • You have an application that you wish to build. This guide assumes a Go application, but you can use an application of your choice.

Example

The following code snippet demonstrates a multi-stage build.

package main

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

func main() {
// create dagger client
ctx := context.Background()
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
panic(err)
}
defer client.Close()

// get host directory
project := client.Host().Directory(".")

// build app
builder := client.Container().
From("golang:latest").
WithDirectory("/src", project).
WithWorkdir("/src").
WithEnvVariable("CGO_ENABLED", "0").
WithExec([]string{"go", "build", "-o", "myapp"})

// publish binary on alpine base
prodImage := client.Container().
From("alpine").
WithFile("/bin/myapp", builder.File("/src/myapp")).
WithEntrypoint([]string{"/bin/myapp"})

addr, err := prodImage.Publish(ctx, "localhost:5000/multistage")
if err != nil {
panic(err)
}

fmt.Println(addr)
}

This code listing starts by creating a Dagger client and loading the project to be built. It obtains a reference to the project and then builds the application by using the golang:latest image to mount the source directory, sets CGO_ENABLED= since the binary will be published on alpine, and executes go build.

Next, the multi-stage build is achieved by transferring the build artifact from the builder image to a runtime image based on alpine. The steps are:

  • Create a new container image which will be used as the runtime image.
  • Transfer the build artifact from the builder image to the new container image.
  • Set the container entrypoint to the application so that it is executed by default when the container runs.

The final optimized image can now be pushed to a registry and deployed!

Conclusion

This tutorial walked you through the process of performing a multi-stage build with Docker.

Use the API Key Concepts page and the Go, Node.js and Python SDK References to learn more about Dagger.