Skip to main content

Container

The Container type represents the state of an OCI-compatible container. This Container object is not merely a string referencing an image on a remote registry. It is the actual state of a container, managed by the Dagger Engine, and passed to a Dagger Function's code as if it were just another variable.

Common operations

Some of the common operations used on the Container type include:

FieldDescription
fromInitializes the container from a specified base image
asServiceTurns the container into a Service
asTarballReturns a serialized tarball of the container as a File
export / importWrites / reads the container as an OCI tarball to / from a file path on the host
publishPublishes the container image to a registry
stdout / stderrReturns the output / error stream of the last executed command
withDirectory / withMountedDirectoryReturns the container plus a directory copied / mounted at the given path
withEntrypointReturns the container with a custom entrypoint command
withExecReturns the container after executing a command inside it
withFile / withMountedFileReturns the container plus a file copied / mounted at the given path
withMountedCacheReturns the container plus a cache volume mounted at the given path
withRegistryAuthReturns the container with registry authentication configured
withWorkdirReturns the container configured with a specific working directory
withServiceBindingReturns the container with runtime dependency on another Service
terminalOpens an interactive terminal for this container

Examples

Build image from Dockerfile

The following Dagger Function builds an image from a Dockerfile.

package main

import (
"context"

"dagger/my-module/internal/dagger"
)

type MyModule struct{}

// Build and publish image from existing Dockerfile
func (m *MyModule) Build(
ctx context.Context,
// location of directory containing Dockerfile
src *dagger.Directory,
) (string, error) {
ref, err := src.
DockerBuild(). // build from Dockerfile
Publish(ctx, "ttl.sh/hello-dagger")

if err != nil {
return "", err
}

return ref, nil
}

Example

Build and publish an image from an existing Dockerfile

dagger -c 'build https://github.com/dockersamples/python-flask-redis'

Build image from Dockerfile using different build context

The following function builds an image from a Dockerfile with a build context that is different than the current working directory.

package main

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

type MyModule struct{}

// Build and publish image from Dockerfile using a build context directory
// in a different location than the current working directory
func (m *MyModule) Build(
ctx context.Context,
// location of source directory
src *dagger.Directory,
// location of Dockerfile
dockerfile *dagger.File,
) (string, error) {

// get build context with dockerfile added
workspace := dag.Container().
WithDirectory("/src", src).
WithWorkdir("/src").
WithFile("/src/custom.Dockerfile", dockerfile).
Directory("/src")

// build using Dockerfile and publish to registry
ref, err := dag.Container().
Build(workspace, dagger.ContainerBuildOpts{
Dockerfile: "custom.Dockerfile",
}).
Publish(ctx, "ttl.sh/hello-dagger")

if err != nil {
return "", err
}

return ref, nil
}

Example

Build an image from the source code in https://github.com/dockersamples/python-flask-redis using the Dockerfile from a different build context, at https://github.com/vimagick/dockerfiles#master:registry-cli/Dockerfile:

dagger -c 'build https://github.com/dockersamples/python-flask-redis https://github.com/vimagick/dockerfiles#master:registry-cli/Dockerfile'

Debug workflows with the interactive terminal

Dagger provides two features that can help greatly when trying to debug a workflow - opening an interactive terminal session at the failure point, or at explicit breakpoints throughout your workflow code. All context is available at the point of failure. Multiple terminals are supported in the same Dagger Function; they will open in sequence.

The following Dagger Function opens an interactive terminal session at different stages in a Dagger workflow to debug a container build.

package main

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

type MyModule struct{}

func (m *MyModule) Container() *dagger.Container {
return dag.Container().
From("alpine:latest").
Terminal().
WithExec([]string{"sh", "-c", "echo hello world > /foo && cat /foo"}).
Terminal()
}

Example

Execute a Dagger Function to build a container, and open an interactive terminal at two different points in the build process. The interactive terminal enables you to inspect the container filesystem and environment "live", during the build process.

dagger -c container

Inspect directories and files

The following Dagger Function clones Dagger's GitHub repository and opens an interactive terminal session to inspect it. Under the hood, this creates a new container (defaults to alpine) and starts a shell, mounting the repository directory inside.

package main

import (
"context"
)

type MyModule struct{}

func (m *MyModule) SimpleDirectory(ctx context.Context) (string, error) {
return dag.
Git("https://github.com/dagger/dagger.git").
Head().
Tree().
Terminal().
File("README.md").
Contents(ctx)
}

The container created to mount the directory can be customized using additional options. The following Dagger Function revised the previous example to demonstrate this, using an ubuntu container image and bash shell instead of the defaults.

package main

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

type MyModule struct{}

func (m *MyModule) AdvancedDirectory(ctx context.Context) (string, error) {
return dag.
Git("https://github.com/dagger/dagger.git").
Head().
Tree().
Terminal(dagger.DirectoryTerminalOpts{
Container: dag.Container().From("ubuntu"),
Cmd: []string{"/bin/bash"},
}).
File("README.md").
Contents(ctx)
}

Example

  • Execute a Dagger Function to clone Dagger's GitHub repository and open a terminal session in the repository directory:

    dagger -c simple-directory
  • Execute another Dagger Function that does the same as the previous one, except using an ubuntu container image as base and initializing the terminal with the bash shell:

    dagger -c advanced-directory

Add OCI annotations to image

The following Dagger Function adds OpenContainer Initiative (OCI) annotations to an image.

package main

import (
"context"
"fmt"
"math"
"math/rand/v2"
)

type MyModule struct{}

// Build and publish image with OCI annotations
func (m *MyModule) Build(ctx context.Context) (string, error) {
address, err := dag.Container().
From("alpine:latest").
WithExec([]string{"apk", "add", "git"}).
WithWorkdir("/src").
WithExec([]string{"git", "clone", "https://github.com/dagger/dagger", "."}).
WithAnnotation("org.opencontainers.image.authors", "John Doe").
WithAnnotation("org.opencontainers.image.title", "Dagger source image viewer").
Publish(ctx, fmt.Sprintf("ttl.sh/custom-image-%.0f", math.Floor(rand.Float64()*10000000))) //#nosec
if err != nil {
return "", err
}
return address, nil
}

Example

Build and publish an image with OCI annotations:

dagger -c build

Add OCI labels to image

The following Dagger Function adds OpenContainer Initiative (OCI) labels to an image.

package main

import (
"context"
"time"
)

type MyModule struct{}

// Build and publish image with OCI labels
func (m *MyModule) Build(
ctx context.Context,
) (string, error) {
ref, err := dag.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").
Publish(ctx, "ttl.sh/my-alpine")

if err != nil {
return "", err
}

return ref, nil
}

Example

Build and publish an image with OCI labels:

dagger -c build