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.
- Go
 - Python
 - TypeScript
 - PHP
 
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))
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
    @function
    async def publish(
        self,
        registry: Annotated[str, Doc("Registry address")],
        username: Annotated[str, Doc("Registry username")],
        password: Annotated[dagger.Secret, Doc("Registry password")],
    ) -> str:
        """Publish a container image to a private registry"""
        return await (
            dag.container()
            .from_("nginx:1.23-alpine")
            .with_new_file(
                "/usr/share/nginx/html/index.html",
                "Hello from Dagger!",
                permissions=0o400,
            )
            .with_registry_auth(registry, username, password)
            .publish(f"{registry}/{username}/my-nginx")
        )
import { dag, Secret, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
  /**
   * Publish a container image to a private registry
   */
  @func()
  async publish(
    /**
     * Registry address
     */
    registry: string,
    /**
     * Registry username
     */
    username: string,
    /**
     * Registry password
     */
    password: Secret,
  ): Promise<string> {
    return await dag
      .container()
      .from("nginx:1.23-alpine")
      .withNewFile("/usr/share/nginx/html/index.html", "Hello from Dagger!", {
        permissions: 0o400,
      })
      .withRegistryAuth(registry, username, password)
      .publish(`${registry}/${username}/my-nginx`)
  }
}
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\DaggerFunction;
use Dagger\Attribute\DaggerObject;
use Dagger\Attribute\Doc;
use Dagger\Container;
use Dagger\File;
use Dagger\Secret;
use function Dagger\dag;
#[DaggerObject]
class MyModule
{
    #[DaggerFunction]
    #[Doc('Publish a container image to a private registry')]
    public function publish(
        #[Doc('registry address')]
        string $registry,
        #[Doc('registry username')]
        string $username,
        #[Doc('registry password')]
        Secret $password,
    ): string {
        return dag()
            ->container()
            ->from('nginx:1.23-alpine')
            ->withNewFile('/usr/share/nginx/html/index.html', 'Hello from Dagger!', 400)
            ->withRegistryAuth($registry, $username, $password)
            ->publish("$registry/$username/my-nginx");
    }
}
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:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c 'publish docker.io user env://PASSWORD'
publish docker.io user env://PASSWORD
dagger call publish --registry=docker.io --username=user --password=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:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c 'publish ghcr.io user env://PASSWORD'
publish ghcr.io user env://PASSWORD
dagger call publish --registry=ghcr.io --username=user --password=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.
- Go
 - Python
 - TypeScript
 - PHP
 
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
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
    @function
    async def publish(
        self,
        registry: Annotated[str, Doc("Registry address")],
        username: Annotated[str, Doc("Registry username")],
        password: Annotated[dagger.Secret, Doc("Registry password")],
    ) -> list[str]:
        """Tag a container image multiple times and publish it to a private registry"""
        tags = ["latest", "1.0-alpine", "1.0", "1.0.0"]
        addr = []
        container = (
            dag.container()
            .from_("nginx:1.23-alpine")
            .with_new_file(
                "/usr/share/nginx/html/index.html",
                "Hello from Dagger!",
                permissions=0o400,
            )
            .with_registry_auth(registry, username, password)
        )
        for tag in tags:
            a = await container.publish(f"{registry}/{username}/my-nginx:{tag}")
            addr.append(a)
        return addr
import { dag, Secret, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
  /**
   * Tag a container image multiple times and publish it to a private registry
   */
  @func()
  async publish(
    /**
     * Registry address
     */
    registry: string,
    /**
     * Registry username
     */
    username: string,
    /**
     * Registry password
     */
    password: Secret,
  ): Promise<string[]> {
    const tags = ["latest", "1.0-alpine", "1.0", "1.0.0"]
    const addr: string[] = []
    const container = dag
      .container()
      .from("nginx:1.23-alpine")
      .withNewFile("/usr/share/nginx/html/index.html", "Hello from Dagger!", {
        permissions: 0o400,
      })
      .withRegistryAuth(registry, username, password)
    for (const tag in tags) {
      const a = await container.publish(
        `${registry}/${username}/my-nginx:${tags[tag]}`,
      )
      addr.push(a)
    }
    return addr
  }
}
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\DaggerFunction;
use Dagger\Attribute\DaggerObject;
use Dagger\Attribute\Doc;
use Dagger\Container;
use Dagger\File;
use Dagger\Secret;
use function Dagger\dag;
#[DaggerObject]
class MyModule
{
    #[DaggerFunction]
    #[Doc('Tag a container image multiple times and publish it to a private registry')]
    public function publish(
        #[Doc('registry address')]
        string $registry,
        #[Doc('registry username')]
        string $username,
        #[Doc('registry password')]
        Secret $password,
    ): string {
        $tags = ['latest', '1.0-alpine', '1.0', '1.0.0'];
        $address = [];
        $container = dag()
            ->container()
            ->from('nginx:1.23-alpine')
            ->withNewFile('/usr/share/nginx/html/index.html', 'Hello from Dagger!', 400)
            ->withRegistryAuth($registry, $username, $password);
        foreach ($tags as $tag) {
          $a = $container->publish("$registry/$username/my-nginx:$tag");
          $address[] = $a;
        }
        return implode(',', $address);
    }
}
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:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c 'publish docker.io user env://PASSWORD'
publish docker.io user env://PASSWORD
dagger call publish --registry=docker.io --username=user --password=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:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c 'publish ghcr.io user env://PASSWORD'
publish ghcr.io user env://PASSWORD
dagger call publish --registry=ghcr.io --username=user --password=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.
- Go
 - Python
 - TypeScript
 - PHP
 
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"})
}
import dagger
from dagger import dag, function, object_type
@object_type
class MyModule:
    @function
    def base(self) -> dagger.Container:
        """Return a container"""
        return (
            dag.container()
            .from_("alpine:latest")
            .with_exec(["mkdir", "/src"])
            .with_exec(["touch", "/src/foo", "/src/bar"])
        )
import { dag, Container, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
  /**
   * Return a container
   */
  @func()
  base(): Container {
    return dag
      .container()
      .from("alpine:latest")
      .withExec(["mkdir", "/src"])
      .withExec(["touch", "/src/foo", "/src/bar"])
  }
}
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\DaggerFunction;
use Dagger\Attribute\DaggerObject;
use Dagger\Attribute\Doc;
use Dagger\Container;
use function Dagger\dag;
#[DaggerObject]
class MyModule
{
    #[DaggerFunction]
    #[Doc('Return a container')]
    public function base(): Container 
    {
        return dag()
            ->container()
            ->from('alpine:latest')
            ->withExec(['mkdir', '/src'])
            ->withExec(['touch', '/src/foo', '/src/bar']);
    }
}
Examples​
Load the container image returned by the Dagger Function into Docker:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c 'base | export-image myimage'
base | export-image myimage
dagger call base export-image --name myimage
Load the container image returned by the Dagger Function as a tarball to the host fileysystem:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c 'base | export /home/admin/mycontainer.tgz'
base | export /home/admin/mycontainer.tgz
dagger call base export --path=/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.
- Go
 - Python
 - TypeScript
 - PHP
 
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)
}
from dagger import dag, function, object_type
@object_type
class MyModule:
    @function
    async def set_env_var(self) -> str:
        """Set a single environment variable in a container"""
        return await (
            dag.container()
            .from_("alpine")
            .with_env_variable("ENV_VAR", "VALUE")
            .with_exec(["env"])
            .stdout()
        )
import { dag, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
  /**
   * Set a single environment variable in a container
   */
  @func()
  async setEnvVar(): Promise<string> {
    return await dag
      .container()
      .from("alpine")
      .withEnvVariable("ENV_VAR", "VALUE")
      .withExec(["env"])
      .stdout()
  }
}
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\DaggerFunction;
use Dagger\Attribute\DaggerObject;
use Dagger\Directory;
use function Dagger\dag;
#[DaggerObject]
class MyModule
{
    // Set a single environment variable in a container
    #[DaggerFunction]
    public function setEnvVar(): string
    {
        return dag()
            ->container()
            ->from('alpine')
            ->withEnvVariable('ENV_VAR', 'VALUE')
            ->withExec(['env'])
            ->stdout();
    }
}
The following Dagger Function demonstrates how to set multiple environment variables in a container.
- Go
 - Python
 - TypeScript
 
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
	}
}
import dagger
from dagger import dag, function, object_type
@object_type
class MyModule:
    @function
    async def set_env_vars(self) -> str:
        """Set environment variables in a container"""
        return await (
            dag.container()
            .from_("alpine")
            .with_(
                self.env_variables(
                    [
                        ("ENV_VAR_1", "VALUE 1"),
                        ("ENV_VAR_2", "VALUE 2"),
                        ("ENV_VAR_3", "VALUE 3"),
                    ]
                )
            )
            .with_exec(["env"])
            .stdout()
        )
    def env_variables(self, envs: list[tuple[str, str]]):
        def env_variables_inner(ctr: dagger.Container):
            for key, value in envs:
                ctr = ctr.with_env_variable(key, value)
            return ctr
        return env_variables_inner
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
  /**
   * Set environment variables in a container
   */
  @func()
  async setEnvVars(): Promise<string> {
    return await dag
      .container()
      .from("alpine")
      .with(
        envVariables([
          ["ENV_VAR_1", "VALUE 1"],
          ["ENV_VAR_2", "VALUE 2"],
          ["ENV_VAR_3", "VALUE_3"],
        ]),
      )
      .withExec(["env"])
      .stdout()
  }
}
function envVariables(envs: Array<[string, string]>) {
  return (c: Container): Container => {
    for (const [key, value] of envs) {
      c = c.withEnvVariable(key, value)
    }
    return c
  }
}
Example​
Set a single environment variable in a container:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c set-env-var
set-env-var
dagger call set-env-var
Set multiple environment variables in a container:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c set-env-vars
set-env-vars
dagger call set-env-vars