Skip to main content

Programmable Pipelines

Dagger provides a specialized container engine for application delivery pipelines, enabling you to replace your YAML-based CI/CD workflows with code in your favorite programming language. This allows you to build your delivery pipelines in the same language as your application and benefit from your language's existing ecosystem for tooling and best practices.

Dagger Functions are the fundamental unit of computing in Dagger. Dagger Functions let you encapsulate common project operations or workflows, such as "pull a container image", "copy a file", and "forward a TCP port", into portable, reusable program code with clear inputs and outputs.

Here's an example of a Dagger Function that builds and containerizes a Go application:

package main

import (
"context"
"dagger/my-module/internal/dagger"

"dagger.io/dagger/dag"
)

type MyModule struct{}

func (m *MyModule) Build(
ctx context.Context,
src *dagger.Directory,
arch string,
os string,
) *dagger.Container {
return dag.Container().
From("golang:1.21").
WithMountedDirectory("/src", src).
WithWorkdir("/src").
WithEnvVariable("GOARCH", arch).
WithEnvVariable("GOOS", os).
WithEnvVariable("CGO_ENABLED", "0").
WithExec([]string{"go", "build", "-o", "build/"})
}

See it in action:

Build

This Dagger Function returns a "just-in-time container" - a transient artifact generated by stringing together calls to the Dagger API using program code. Dagger is commonly used to produce artifacts like these, including binary files, containers, generated code or documentation, lint or vulnerability scan reports, and so on.

Just-in-time artifacts themselves expose additional functions that allow them to be inspected or processed further. So, for example, if a Dagger Function returns a just-in-time file, you can continue "chaining" operations on it by calling functions exposed by that artifact type, such as exporting it to the host filesystem, modifying it, mounting it into a container, and so on. This feature makes it possible to develop complex delivery pipelines with relatively little effort.

Here's an example of calling the same Dagger Function again, but this time chaining an additional operation on the just-in-time container to publish it to a container registry:

Build and publish

Here's the same Dagger Function again, modified to return the compiled binary as a just-in-time file. The file is then exported to the host via an additional, chained function call:

package main

import (
"context"
"dagger/my-module/internal/dagger"

"dagger.io/dagger/dag"
)

type MyModule struct{}

func (m *MyModule) Build(
ctx context.Context,
src *dagger.Directory,
arch string,
os string,
) *dagger.File {
return dag.Container().
From("golang:1.21").
WithMountedDirectory("/src", src).
WithWorkdir("/src").
WithEnvVariable("GOARCH", arch).
WithEnvVariable("GOOS", os).
WithEnvVariable("CGO_ENABLED", "0").
WithExec([]string{"go", "build", "-o", "build/"}).
File("/src/build/hello")
}

See it in action:

Build and export

tip

Dagger leverages GraphQL's lazy evaluation model to optimize and parallelize query execution for maximum speed and performance.