Skip to main content

Cookbook

Filesystem

List host directory contents

The following code listing obtains a reference to the host working directory and lists the directory's contents.

package main

import (
"context"
"fmt"
"log"
"os"

"dagger.io/dagger"
)

func main() {
ctx := context.Background()

client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
log.Println(err)
return
}
defer client.Close()

entries, err := client.Host().Directory(".").Entries(ctx)
if err != nil {
log.Println(err)
return
}

fmt.Println(entries)
}

Learn more

Get host directory with filters

The following code listing obtains a reference to the host working directory containing all files except *.txt files.

package main

import (
"context"
"fmt"
"log"
"os"

"dagger.io/dagger"
)

func main() {
os.WriteFile("foo.txt", []byte("1"), 0600)
os.WriteFile("bar.txt", []byte("2"), 0600)
os.WriteFile("baz.rar", []byte("3"), 0600)

ctx := context.Background()

client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
log.Println(err)
return
}
defer client.Close()

entries, err := client.Host().Directory(".", dagger.HostDirectoryOpts{
Exclude: []string{"*.txt"},
}).Entries(ctx)
if err != nil {
log.Println(err)
return
}

fmt.Println(entries)
}

The following code listing obtains a reference to the host working directory containing only *.rar files.

package main

import (
"context"
"fmt"
"log"
"os"

"dagger.io/dagger"
)

func main() {
os.WriteFile("foo.txt", []byte("1"), 0600)
os.WriteFile("bar.txt", []byte("2"), 0600)
os.WriteFile("baz.rar", []byte("3"), 0600)

ctx := context.Background()

client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
log.Println(err)
return
}
defer client.Close()

entries, err := client.Host().Directory(".", dagger.HostDirectoryOpts{
Include: []string{"*.rar"},
}).Entries(ctx)
if err != nil {
log.Println(err)
return
}

fmt.Println(entries)
}

The following code listing obtains a reference to the host working directory containing all files except *.rar files.

package main

import (
"context"
"fmt"
"log"
"os"

"dagger.io/dagger"
)

func main() {
os.WriteFile("foo.txt", []byte("1"), 0600)
os.WriteFile("bar.txt", []byte("2"), 0600)
os.WriteFile("baz.rar", []byte("3"), 0600)

ctx := context.Background()

client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
log.Println(err)
return
}
defer client.Close()

entries, err := client.Host().Directory(".", dagger.HostDirectoryOpts{
Include: []string{"*.*"},
Exclude: []string{"*.rar"},
}).Entries(ctx)
if err != nil {
log.Println(err)
return
}

fmt.Println(entries)
}

Learn more

Mount and read host directory in container

The following code listing mounts a host directory in a container at the /host container path and then reads the contents of the mounted directory.

package main

import (
"context"
"fmt"
"log"
"os"

"dagger.io/dagger"
)

func main() {
ctx := context.Background()

client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
log.Println(err)
return
}
defer client.Close()

contents, err := client.Container().
From("alpine:latest").
WithDirectory("/host", client.Host().Directory(".")).
WithExec([]string{"ls", "/host"}).
Stdout(ctx)
if err != nil {
log.Println(err)
return
}

fmt.Println(contents)
}

Mount and write to host directory from container

The following code listing shows how to mount a host directory in a container at the /host container path, write a file to it, and then export the modified directory back to the host:

note

Modifications made to a host directory mounted in a container do not appear on the host. Data flows only one way between Dagger operations, because they are connected in a DAG. To write modifications back to the host directory, you must explicitly export the directory back to the host filesystem.

package main

import (
"context"
"fmt"
"log"
"os"

"dagger.io/dagger"
)

func main() {
ctx := context.Background()

client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
log.Println(err)
return
}
defer client.Close()

contents, err := client.Container().
From("alpine:latest").
WithDirectory("/host", client.Host().Directory("/tmp/sandbox")).
WithExec([]string{"/bin/sh", "-c", `echo foo > /host/bar`}).
Directory("/host").
Export(ctx, "/tmp/sandbox")
if err != nil {
log.Println(err)
return
}

fmt.Println(contents)
}

Learn more

Add Git repository as directory to container

The following code listing adds a remote Git repository branch to a container as a directory at the /src container path and then executes a command in the container to list the directory contents.

package main

import (
"context"
"fmt"
"log"
"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 repository at specified branch
project := client.
Git("https://github.com/dagger/dagger").
Branch("main").
Tree()

// return container with repository
// at /src path
contents, err := client.Container().
From("alpine:latest").
WithDirectory("/src", project).
WithWorkdir("/src").
WithExec([]string{"ls", "/src"}).
Stdout(ctx)
if err != nil {
log.Println(err)
return
}

fmt.Println(contents)
}

Add Git repository as directory to container with filters

The following code listing adds a remote Git repository branch as a directory at the /src container path, excluding *.md files.

package main

import (
"context"
"fmt"
"log"
"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 repository at specified branch
project := client.
Git("https://github.com/dagger/dagger").
Branch("main").
Tree()

// return container with repository
// at /src path
// excluding *.md files
contents, err := client.Container().
From("alpine:latest").
WithDirectory("/src", project, dagger.ContainerWithDirectoryOpts{
Exclude: []string{"*.md"},
}).
WithWorkdir("/src").
WithExec([]string{"ls", "/src"}).
Stdout(ctx)
if err != nil {
log.Println(err)
return
}

fmt.Println(contents)
}

The following code listing adds a remote Git repository branch as a directory at the /src container path, including only *.md files.

package main

import (
"context"
"fmt"
"log"
"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 repository at specified branch
project := client.
Git("https://github.com/dagger/dagger").
Branch("main").
Tree()

// return container with repository
// at /src path
// including only *.md files
contents, err := client.Container().
From("alpine:latest").
WithDirectory("/src", project, dagger.ContainerWithDirectoryOpts{
Include: []string{"*.md"},
}).
WithWorkdir("/src").
WithExec([]string{"ls", "/src"}).
Stdout(ctx)
if err != nil {
log.Println(err)
return
}

fmt.Println(contents)
}

The following code listing adds a remote Git repository branch as a directory at the /src container path, including all *.md files except README.md.

package main

import (
"context"
"fmt"
"log"
"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 repository at specified branch
project := client.
Git("https://github.com/dagger/dagger").
Branch("main").
Tree()

// return container with repository
// at /src path
// include all *.md files except README.md
contents, err := client.Container().
From("alpine:latest").
WithDirectory("/src", project, dagger.ContainerWithDirectoryOpts{
Include: []string{"*.md"},
Exclude: []string{"README.md"},
}).
WithWorkdir("/src").
WithExec([]string{"ls", "/src"}).
Stdout(ctx)
if err != nil {
log.Println(err)
return
}

fmt.Println(contents)
}

Builds

Perform multi-stage build

The following code listing performs 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)
}

Learn more

Perform matrix build

The following code listing builds separate images for multiple OS and CPU architecture combinations.

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

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

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 {
panic(err)
}

// 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
From("golang:latest").
// mount source code into golang image
WithDirectory("/src", src).
WithWorkdir("/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 {
panic(err)
}

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

Learn more

Build multi-arch image

The following code listing builds a single image for different CPU architectures using native emulation.

package main

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

// the platforms to build for and push in a multi-platform image
var platforms = []dagger.Platform{
"linux/amd64", // a.k.a. x86_64
"linux/arm64", // a.k.a. aarch64
"linux/s390x", // a.k.a. IBM S/390
}

// the container registry for the multi-platform image
const imageRepo = "localhost/testrepo:latest"

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

// the git repository containing code for the binary to be built
gitRepo := client.Git("https://github.com/dagger/dagger.git").
Branch("086862926433e19e1f24cd709e6165c36bdb2633").
Tree()

platformVariants := make([]*dagger.Container, 0, len(platforms))
for _, platform := range platforms {
// pull the golang image for this platform
ctr := client.Container(dagger.ContainerOpts{Platform: platform})
ctr = ctr.From("golang:1.20-alpine")

// mount in source code
ctr = ctr.WithDirectory("/src", gitRepo)

// mount in an empty dir where the built binary will live
ctr = ctr.WithDirectory("/output", client.Directory())

// ensure the binary will be statically linked and thus executable
// in the final image
ctr = ctr.WithEnvVariable("CGO_ENABLED", "0")

// build the binary and put the result at the mounted output
// directory
ctr = ctr.WithWorkdir("/src")
ctr = ctr.WithExec([]string{
"go", "build",
"-o", "/output/dagger",
"/src/cmd/dagger",
})

// select the output directory
outputDir := ctr.Directory("/output")

// wrap the output directory in a new empty container marked
// with the same platform
binaryCtr := client.
Container(dagger.ContainerOpts{Platform: platform}).
WithRootfs(outputDir)
platformVariants = append(platformVariants, binaryCtr)
}

// publishing the final image uses the same API as single-platform
// images, but now additionally specify the `PlatformVariants`
// option with the containers built before.
imageDigest, err := client.
Container().
Publish(ctx, imageRepo, dagger.ContainerPublishOpts{
PlatformVariants: platformVariants,
// Some registries may require explicit use of docker mediatypes
// rather than the default OCI mediatypes
// MediaTypes: dagger.Dockermediatypes,
})
if err != nil {
panic(err)
}
fmt.Println("Pushed multi-platform image w/ digest: ", imageDigest)
}

Learn more

Build multi-arch image with cross-compilation

The following code listing builds a single image for different CPU architectures using cross-compilation.

package main

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
platformFormat "github.com/containerd/containerd/platforms"
)

var platforms = []dagger.Platform{
"linux/amd64", // a.k.a. x86_64
"linux/arm64", // a.k.a. aarch64
"linux/s390x", // a.k.a. IBM S/390
}

// the container registry for the multi-platform image
const imageRepo = "localhost/testrepo:latest"

// util that returns the architecture of the provided platform
func architectureOf(platform dagger.Platform) string {
return platformFormat.MustParse(string(platform)).Architecture
}

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

gitRepo := client.Git("https://github.com/dagger/dagger.git").
Branch("086862926433e19e1f24cd709e6165c36bdb2633").
Tree()

platformVariants := make([]*dagger.Container, 0, len(platforms))
for _, platform := range platforms {
// pull the golang image for the *host platform*. This is
// accomplished by just not specifying a platform; the default
// is that of the host.
ctr := client.Container()
ctr = ctr.From("golang:1.20-alpine")

// mount in our source code
ctr = ctr.WithDirectory("/src", gitRepo)

// mount in an empty dir to put the built binary
ctr = ctr.WithDirectory("/output", client.Directory())

// ensure the binary will be statically linked and thus executable
// in the final image
ctr = ctr.WithEnvVariable("CGO_ENABLED", "0")

// configure the go compiler to use cross-compilation targeting the
// desired platform
ctr = ctr.WithEnvVariable("GOOS", "linux")
ctr = ctr.WithEnvVariable("GOARCH", architectureOf(platform))

// build the binary and put the result at the mounted output
// directory
ctr = ctr.WithWorkdir("/src")
ctr = ctr.WithExec([]string{
"go", "build",
"-o", "/output/dagger",
"/src/cmd/dagger",
})
// select the output directory
outputDir := ctr.Directory("/output")

// wrap the output directory in a new empty container marked
// with the platform
binaryCtr := client.
Container(dagger.ContainerOpts{Platform: platform}).
WithRootfs(outputDir)
platformVariants = append(platformVariants, binaryCtr)
}

// publishing the final image uses the same API as single-platform
// images, but now additionally specify the `PlatformVariants`
// option with the containers built before.
imageDigest, err := client.
Container().
Publish(ctx, imageRepo, dagger.ContainerPublishOpts{
PlatformVariants: platformVariants,
// Some registries may require explicit use of docker mediatypes
// rather than the default OCI mediatypes
// MediaTypes: dagger.Dockermediatypes,
})
if err != nil {
panic(err)
}
fmt.Println("published multi-platform image with digest", imageDigest)
}

Learn more

Build image from Dockerfile

The following code listing builds an image from a Dockerfile in the current working directory on the host.

package main

import (
"context"
"fmt"
"math"
"math/rand"
"os"

"dagger.io/dagger"
)

func main() {
ctx := context.Background()

// initialize Dagger client
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
panic(err)
}
defer client.Close()

contextDir := client.Host().Directory(".")

ref, err := contextDir.
DockerBuild().
Publish(ctx, fmt.Sprintf("ttl.sh/hello-dagger-%.0f", math.Floor(rand.Float64()*10000000))) //#nosec
if err != nil {
panic(err)
}

fmt.Printf("Published image to :%s\n", ref)
}

Learn more

Build image from Dockerfile using different build context

The following code listing builds an image from a Dockerfile using a build context directory in a different location than the current working directory.

package main

import (
"context"
"fmt"
"math"
"math/rand"
"os"

"dagger.io/dagger"
)

func main() {
ctx := context.Background()

// initialize Dagger client
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
panic(err)
}
defer client.Close()

// get build context directory
contextDir := client.Host().Directory("/projects/myapp")

// get Dockerfile in different filesystem location
dockerfilePath := "/data/myapp/custom.Dockerfile"
dockerfile := client.Host().File(dockerfilePath)

// add Dockerfile to build context directory
workspace := contextDir.WithFile("custom.Dockerfile", dockerfile)

// build using Dockerfile
// publish the resulting container to a registry
ref, err := client.
Container().
Build(workspace, dagger.ContainerBuildOpts{
Dockerfile: "custom.Dockerfile",
}).
Publish(ctx, fmt.Sprintf("ttl.sh/hello-dagger-%.0f", math.Floor(rand.Float64()*10000000))) //#nosec
if err != nil {
panic(err)
}

fmt.Printf("Published image to :%s\n", ref)
}

Learn more

Add OCI annotations to image

The following code listing adds OpenContainer Initiative (OCI) annotations to an image.

package main

import (
"context"
"fmt"
"os"
"time"

"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()

// create and publish image with annotations
ctr := client.Container().
From("alpine").
WithLabel("org.opencontainers.image.title", "my-alpine").
WithLabel("org.opencontainers.image.version", "1.0").
WithLabel("org.opencontainers.image.created", time.Now().String()).
WithLabel("org.opencontainers.image.source", "https://github.com/alpinelinux/docker-alpine").
WithLabel("org.opencontainers.image.licenses", "MIT")

addr, err := ctr.Publish(ctx, "ttl.sh/my-alpine")
if err != nil {
panic(err)
}

fmt.Println(addr)
}

Define build-time variables

The following code listing defines various environment variables for build purposes.

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

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

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 {
panic(err)
}

// 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
From("golang:latest").
// mount source code into golang image
WithDirectory("/src", src).
WithWorkdir("/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 {
panic(err)
}

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

Learn more

Access private Git repository

The following code listing demonstrates how to access a private Git repository using SSH.

package main

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

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

// Retrieve path of authentication agent socket from host
sshAgentPath := os.Getenv("SSH_AUTH_SOCK")

// Private repository with a README.md file at the root.
readme, err := client.
Git("git@private-repository.git").
Branch("main").
Tree(
dagger.GitRefTreeOpts{
SSHAuthSocket: client.Host().UnixSocket(sshAgentPath),
},
).
File("README.md").
Contents(ctx)

if err != nil {
panic(err)
}

fmt.Println("readme", readme)
}

Use transient database for application tests

The following code listing creates a temporary MariaDB database service and binds it to an application container for unit/integration testing.

package main

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

func main() {
ctx := context.Background()

// create Dagger client
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))

if err != nil {
panic(err)
}
defer client.Close()

// get MariaDB base image
mariadb := client.Container().
From("mariadb:10.11.2").
WithEnvVariable("MARIADB_USER", "user").
WithEnvVariable("MARIADB_PASSWORD", "password").
WithEnvVariable("MARIADB_DATABASE", "drupal").
WithEnvVariable("MARIADB_ROOT_PASSWORD", "root").
WithExposedPort(3306)

// get Drupal base image
// install additional dependencies
drupal := client.Container().
From("drupal:10.0.7-php8.2-fpm").
WithExec([]string{"composer", "require", "drupal/core-dev", "--dev", "--update-with-all-dependencies"})

// add service binding for MariaDB
// run kernel tests using PHPUnit
test, err := drupal.
WithServiceBinding("db", mariadb).
WithEnvVariable("SIMPLETEST_DB", "mysql://user:password@db/drupal").
WithEnvVariable("SYMFONY_DEPRECATIONS_HELPER", "disabled").
WithWorkdir("/opt/drupal/web/core").
WithExec([]string{"../../vendor/bin/phpunit", "-v", "--group", "KernelTests"}).
Stdout(ctx)

if err != nil {
panic(err)
}

fmt.Println(test)
}

Learn more

Invalidate cache

The following code listing demonstrates how to invalidate the Dagger pipeline operations cache and thereby force execution of subsequent pipeline steps, by introducing a volatile time variable at a specific point in the Dagger pipeline.

note

This is a temporary workaround until cache invalidation support is officially added to Dagger.

note

Changes in mounted cache volumes do not invalidate the Dagger pipeline operations cache.

package main

import (
"context"
"fmt"
"os"
"time"

"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()

// invalidate cache to force execution
// of second WithExec() operation
output, err := client.Pipeline("test").
Container().
From("alpine").
WithExec([]string{"apk", "add", "curl"}).
WithEnvVariable("CACHEBUSTER", time.Now().String()).
WithExec([]string{"apk", "add", "zip"}).
Stdout(ctx)
if err != nil {
panic(err)
}
fmt.Println(output)
}

Outputs

Publish image to registry

The following code listing publishes a container image to a remote registry (Docker Hub). Replace the DOCKER-HUB-USERNAME and DOCKER-HUB-PASSWORD placeholders with your Docker Hub username and password respectively.

package main

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

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

// set secret as string value
secret := client.SetSecret("password", "DOCKER-HUB-PASSWORD")

// create container
c := client.Container(dagger.ContainerOpts{Platform: "linux/amd64"}).
From("nginx:1.23-alpine").
WithNewFile("/usr/share/nginx/html/index.html", dagger.ContainerWithNewFileOpts{
Contents: "Hello from Dagger!",
Permissions: 0o400,
})

// use secret for registry authentication
addr, err := c.
WithRegistryAuth("docker.io", "DOCKER-HUB-USERNAME", secret).
Publish(ctx, "DOCKER-HUB-USERNAME/my-nginx")
if err != nil {
panic(err)
}

// print result
fmt.Println("Published at:", addr)
}

Learn more

Export image to host

The following code listing exports a container image from a Dagger pipeline to the host.

package main

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

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

// use NGINX container
// add new webserver index page
c := client.Container(dagger.ContainerOpts{Platform: "linux/amd64"}).
From("nginx:1.23-alpine").
WithNewFile("/usr/share/nginx/html/index.html", dagger.ContainerWithNewFileOpts{
Contents: "Hello from Dagger!",
Permissions: 0o400,
})

// export to host filesystem
val, err := c.Export(ctx, "/tmp/my-nginx.tar")
if err != nil {
panic(err)
}

// print result
fmt.Println("Exported image: ", val)
}

Learn more

Export container directory to host

The following code listing exports the contents of a container directory to the host's temporary directory.

package main

import (
"context"
"fmt"
"log"
"os"
"path/filepath"

"dagger.io/dagger"
)

func main() {
hostdir := os.TempDir()

ctx := context.Background()

client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
log.Println(err)
return
}
defer client.Close()

_, err = client.Container().From("alpine:latest").
WithWorkdir("/tmp").
WithExec([]string{"wget", "https://dagger.io"}).
Directory(".").
Export(ctx, hostdir)
if err != nil {
log.Println(err)
return
}
contents, err := os.ReadFile(filepath.Join(hostdir, "index.html"))
if err != nil {
log.Println(err)
return
}
fmt.Println(string(contents))
}

Learn more

Publish image to registry with multiple tags

The following code listing tags a container image multiple times and publishes it to a remote registry (Docker Hub). Set the Docker Hub username and password as host environment variables named DOCKERHUB_USERNAME and DOCKERHUB_PASSWORD respectively.

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()

// load registry credentials from environment variables
username := os.Getenv("DOCKERHUB_USERNAME")
if username == "" {
panic("DOCKERHUB_USERNAME env var must be set")
}
passwordPlaintext := os.Getenv("DOCKERHUB_PASSWORD")
if passwordPlaintext == "" {
panic("DOCKERHUB_PASSWORD env var must be set")
}
password := client.SetSecret("password", passwordPlaintext)

// define multiple image tags
tags := [4]string{"latest", "1.0-alpine", "1.0", "1.0.0"}

// create and publish image with multiple tags
ctr := client.Container().
From("alpine").
WithRegistryAuth("docker.io", username, password)

for _, tag := range tags {
addr, err := ctr.Publish(ctx, fmt.Sprintf("%s/alpine:%s", username, tag))
if err != nil {
panic(err)
}
fmt.Println("Published: ", addr)
}
}

Secrets

Expose secret via environment variable

The following code listing demonstrates how to inject an environment variable in a container as a secret.

package main

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

func main() {
// initialize Dagger client
ctx := context.Background()

if os.Getenv("GH_SECRET") == "" {
panic("Environment variable GH_SECRET is not set")
}

client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
panic(err)
}
defer client.Close()

// read secret from host variable
secret := client.SetSecret("gh-secret", os.Getenv("GH_SECRET"))

// use secret in container environment
out, err := client.
Container().
From("alpine:3.17").
WithSecretVariable("GITHUB_API_TOKEN", secret).
WithExec([]string{"apk", "add", "curl"}).
WithExec([]string{"sh", "-c", `curl "https://api.github.com/repos/dagger/dagger/issues" --header "Accept: application/vnd.github+json" --header "Authorization: Bearer $GITHUB_API_TOKEN"`}).
Stdout(ctx)
if err != nil {
panic(err)
}

fmt.Println(out)
}

Learn more

Expose secret via file

The following code listing demonstrates how to inject a file in a container as a secret.

package main

import (
"context"
"fmt"
"os"

"dagger.io/dagger"
)

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

// read file
config, err := os.ReadFile("/home/USER/.config/gh/hosts.yml")
if err != nil {
panic(err)
}

// set secret to file contents
secret := client.SetSecret("ghConfig", string(config))

// mount secret as file in container
out, err := client.
Container().
From("alpine:3.17").