Skip to main content

Create a Multi-Build CI Pipeline


The Dagger SDKs makes it easy to build an application for multiple OS and architecture combinations. This guide provides a working example of a CI tool that performs this task.


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.

Dagger pipelines are executed as standard OCI containers. This portability enables you to do very powerful things. For example, if you're a Python developer, you can use the Python SDK to create a pipeline (written in Python) that builds an application written in a different language (Go) without needing to learn that language.


Assume that the Go application to be built is stored in the current directory on the host. The following code listing demonstrates how to build this Go application for multiple OS and architecture combinations using the Dagger SDKs.

// Create a multi-build pipeline for a Go application.
package main

import (


func main() {
println("Building with Dagger")

// define build matrix
geese := []string{"linux", "darwin"}
goarches := []string{"amd64", "arm64"}

ctx := context.Background()
// initialize dagger client
c, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {

// get reference to the local project
src := c.Host().Directory(".")

// create empty directory to put build outputs
outputs := c.Directory()

golang := c.Container().
// get golang image
// mount source code into golang image
WithDirectory("/src", src).

for _, goos := range geese {
for _, goarch := range goarches {
// create a directory for each OS and architecture
path := fmt.Sprintf("build/%s/%s/", goos, goarch)

build := golang.
// set GOARCH and GOOS in the build environment
WithEnvVariable("GOOS", goos).
WithEnvVariable("GOARCH", goarch).
WithExec([]string{"go", "build", "-o", path})

// add build to outputs
outputs = outputs.WithDirectory(path, build.Directory(path))

// write build artifacts to host
ok, err := outputs.Export(ctx, ".")
if err != nil {

if !ok {
panic("did not export files")

This code listing does the following:

  • It defines the build matrix, consisting of two OSs (darwin and linux) and two architectures (amd64 and arm64).
  • It creates a Dagger client with Connect().
  • It uses the client's Host().Directory(".") method to obtain a reference to the current directory on the host. This reference is stored in the src variable.
  • It uses the client's Container().From() method to initialize a new container from a base image. This base image contains all the tooling needed to build the application - in this case, the golang:latest image. This From() method returns a new Container class with the results.
  • It uses the Container.Directory() method to mount the host directory into the container at the /src mount point.
  • It uses the Container.WithWorkdir() method to set the working directory in the container.
  • It iterates over the build matrix, creating a directory in the container for each OS/architecture combination and building the Go application for each such combination. The Go build process is instructed via the GOOS and GOARCH build variables, which are reset for each case via the Container.WithEnvVariable() method.
  • It obtains a reference to the build output directory in the container with the WithDirectory() method, and then uses the Directory.Export() method to write the build directory from the container to the host.


This guide showed you how to build an application for multiple OS and architecture combinations with Dagger.

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