Skip to main content

Container Images

Dagger allows you to build, publish, and export container images, also known as just-in-time artifacts, as part of your Dagger Functions. This section shows you how to work with container images using Dagger with practical examples.

Publish a container image to a private registry​

The following Dagger Function publishes a just-in-time container image to a private registry.

package main

import (
"context"
"fmt"

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

type MyModule struct{}

// Publish a container image to a private registry
func (m *MyModule) Publish(
ctx context.Context,
// Registry address
registry string,
// Registry username
username string,
// Registry password
password *dagger.Secret,
) (string, error) {
return dag.Container().
From("nginx:1.23-alpine").
WithNewFile(
"/usr/share/nginx/html/index.html",
"Hello from Dagger!",
dagger.ContainerWithNewFileOpts{Permissions: 0o400},
).
WithRegistryAuth(registry, username, password).
Publish(ctx, fmt.Sprintf("%s/%s/my-nginx", registry, username))
}

Examples​

Publish a just-in-time container image to Docker Hub, using the account username user and the password set in the PASSWORD environment variable:

dagger -c 'publish docker.io user env://PASSWORD'

Publish a just-in-time container image to GitHub Container Registry, using the account username user and the GitHub personal access token set in the PASSWORD environment variable:

dagger -c 'publish ghcr.io user env://PASSWORD'

Publish a container image to a private registry with multiple tags​

The following Dagger Function tags a just-in-time container image multiple times and publishes it to a private registry.

package main

import (
"context"
"fmt"

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

type MyModule struct{}

// Tag a container image multiple times and publish it to a private registry
func (m *MyModule) Publish(
ctx context.Context,
// Registry address
registry string,
// Registry username
username string,
// Registry password
password *dagger.Secret,
) ([]string, error) {
tags := [4]string{"latest", "1.0-alpine", "1.0", "1.0.0"}
addr := []string{}
ctr := dag.Container().
From("nginx:1.23-alpine").
WithNewFile(
"/usr/share/nginx/html/index.html",
"Hello from Dagger!",
dagger.ContainerWithNewFileOpts{Permissions: 0o400},
).
WithRegistryAuth(registry, username, password)

for _, tag := range tags {
a, err := ctr.Publish(ctx, fmt.Sprintf("%s/%s/my-nginx:%s", registry, username, tag))
if err != nil {
return addr, err
}
addr = append(addr, a)
}
return addr, nil
}

Examples​

Tag and publish a just-in-time container image to Docker Hub, using the account username user and the password set in the PASSWORD environment variable:

dagger -c 'publish docker.io user env://PASSWORD'

Tag and publish a just-in-time container image to GitHub Container Registry, using the account username user and the GitHub personal access token set in the PASSWORD environment variable:

dagger -c 'publish ghcr.io user env://PASSWORD'

Export a container image to the host​

The following Dagger Function returns a just-in-time container. This can be exported to the host as an OCI tarball.

package main

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

type MyModule struct{}

// Return a container
func (m *MyModule) Base() *dagger.Container {
return dag.Container().
From("alpine:latest").
WithExec([]string{"mkdir", "/src"}).
WithExec([]string{"touch", "/src/foo", "/src/bar"})
}

Examples​

Load the container image returned by the Dagger Function into Docker:

dagger -c 'base | export-image myimage'

Load the container image returned by the Dagger Function as a tarball to the host fileysystem:

dagger -c 'base | export /home/admin/mycontainer.tgz'

Set environment variables in a container​

The following Dagger Function demonstrates how to set a single environment variable in a container.

package main

import "context"

type MyModule struct{}

// Set a single environment variable in a container
func (m *MyModule) SetEnvVar(ctx context.Context) (string, error) {
return dag.Container().
From("alpine").
WithEnvVariable("ENV_VAR", "VALUE").
WithExec([]string{"env"}).
Stdout(ctx)
}

The following Dagger Function demonstrates how to set multiple environment variables in a container.

package main

import (
"context"

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

type MyModule struct{}

type EnvVar struct {
Name string
Value string
}

// Set environment variables in a container
func (m *MyModule) SetEnvVars(ctx context.Context) (string, error) {
return dag.Container().
From("alpine").
With(EnvVariables([]*EnvVar{
{"ENV_VAR_1", "VALUE 1"},
{"ENV_VAR_2", "VALUE 2"},
{"ENV_VAR_3", "VALUE 3"},
})).
WithExec([]string{"env"}).
Stdout(ctx)
}

func EnvVariables(envs []*EnvVar) dagger.WithContainerFunc {
return func(c *dagger.Container) *dagger.Container {
for _, e := range envs {
c = c.WithEnvVariable(e.Name, e.Value)
}
return c
}
}

Example​

Set a single environment variable in a container:

dagger -c set-env-var

Set multiple environment variables in a container:

dagger -c set-env-vars

Use volatile variables for exec-time metadata​

withVolatileVariable sets a non-secret environment variable for future withExec calls without invalidating exec cache when only the variable's value changes.

Typical examples include CI and reporting metadata such as:

  • commit SHA
  • branch or ref
  • CI run ID
warning

withVolatileVariable is an expert-only escape hatch. Use it only when you are certain that changing the variable alone must not invalidate cached withExec results. If that assumption is wrong, Dagger may reuse stale or incorrect cached results.

Unlike withEnvVariable, volatile variables:

  • are visible only to future withExec calls
  • are not persisted into the container image config
  • are not returned by envVariable or envVariables
  • are not available to expand: true

Use withEnvVariable for normal container configuration, withSecretVariable for sensitive values, and withVolatileVariable only for exec-time metadata that should not decide cache reuse.