Cookbook
Filesystem
Copy a directory or remote repository to a container
The following Dagger Function accepts a Directory
argument, which could reference either a directory from the local filesystem or from a remote Git repository. It copies the specified directory to the /src
path in a container and returns the modified container.
When working with private Git repositories, ensure that SSH authentication is properly configured on your Dagger host.
- Go
- Python
- TypeScript
package main
import (
"context"
"dagger/my-module/internal/dagger"
)
type MyModule struct{}
// Return a container with a specified directory
func (m *MyModule) CopyDirectory(
ctx context.Context,
// Source directory
source *dagger.Directory,
) *dagger.Container {
return dag.Container().
From("alpine:latest").
WithDirectory("/src", source)
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
@function
def copy_directory(
self, source: Annotated[dagger.Directory, Doc("Source directory")]
) -> dagger.Container:
"""Return a container with a specified directory"""
return dag.container().from_("alpine:latest").with_directory("/src", source)
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Return a container with a specified directory
*/
@func()
copyDirectory(
/**
* Source directory
*/
source: Directory,
): Container {
return dag.container().from("alpine:latest").withDirectory("/src", source)
}
}
Examples
-
Copy the
/myapp
host directory to/src
in the container and return the modified container:dagger call copy-directory --source=./myapp/
-
Copy the public
dagger/dagger
GitHub repository to/src
in the container and return the modified container:dagger call copy-directory --source=github.com/dagger/dagger#main
-
Copy the private
user/foo
GitHub repository to/src
in the container and return the modified container:dagger call copy-directory --source=ssh://git@github.com/user/foo#main
-
Copy the public
dagger/dagger
GitHub repository to/src
in the container and list the contents of the directory:dagger call copy-directory --source=https://github.com/dagger/dagger#main directory --path=/src entries
Modify a copied directory or remote repository in a container
The following Dagger Function accepts a Directory
argument, which could reference either a directory from the local filesystem or from a remote Git repository. It copies the specified directory to the /src
path in a container, adds a file to it, and returns the modified container.
Modifications made to a directory's contents after it is written to a container filesystem do not appear on the source. Data flows only one way between Dagger operations, because they are connected in a DAG. To transfer modifications back to the local host, you must explicitly export the directory back to the host filesystem.
When working with private Git repositories, ensure that SSH authentication is properly configured on your Dagger host.
- Go
- Python
- TypeScript
package main
import (
"context"
"dagger/my-module/internal/dagger"
)
type MyModule struct{}
// Return a container with a specified directory and an additional file
func (m *MyModule) CopyAndModifyDirectory(
ctx context.Context,
// Source directory
source *dagger.Directory,
) *dagger.Container {
return dag.Container().
From("alpine:latest").
WithDirectory("/src", source).
WithExec([]string{"/bin/sh", "-c", `echo foo > /src/foo`})
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
@function
def copy_and_modify_directory(
self, source: Annotated[dagger.Directory, Doc("Source directory")]
) -> dagger.Container:
"""Return a container with a specified directory and an additional file"""
return (
dag.container()
.from_("alpine:latest")
.with_directory("/src", source)
.with_exec(["/bin/sh", "-c", "`echo foo > /src/foo`"])
)
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Return a container with a specified directory and an additional file
*/
@func()
copyAndModifyDirectory(
/**
* Source directory
*/
source: Directory,
): Container {
return dag
.container()
.from("alpine:latest")
.withDirectory("/src", source)
.withExec(["/bin/sh", "-c", "`echo foo > /src/foo`"])
}
}
Examples
-
Copy the
/myapp
host directory to/src
in the container, add a file to it, and return the modified container:dagger call copy-and-modify-directory --source=./myapp/
-
Copy the public
dagger/dagger
GitHub repository to/src
in the container, add a file to it, and return the modified container:dagger call copy-and-modify-directory --source=github.com/dagger/dagger#main
-
Copy the private
user/foo
GitHub repository to/src
in the container, add a file to it, and return the modified container:dagger call copy-and-modify-directory --source=ssh://git@github.com/user/foo#main
-
Copy the public
dagger/dagger
GitHub repository to/src
in the container, add a file to it, and list the contents of the directory:dagger call copy-and-modify-directory --source=https://github.com/dagger/dagger#main directory --path=/src entries
Copy a file to a container
The following Dagger Function accepts a File
argument representing the host file to be copied. It writes the specified file to a container in the /src/
directory and returns the modified container.
- Go
- Python
- TypeScript
package main
import (
"context"
"fmt"
"dagger/my-module/internal/dagger"
)
type MyModule struct{}
// Return a container with a specified file
func (m *MyModule) CopyFile(
ctx context.Context,
// Source file
f *dagger.File,
) *dagger.Container {
name, _ := f.Name(ctx)
return dag.Container().
From("alpine:latest").
WithFile(fmt.Sprintf("/src/%s", name), f)
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
@function
async def copy_file(
self,
f: Annotated[dagger.File, Doc("Source file")],
) -> dagger.Container:
"""Return a container with a specified file"""
name = await f.name()
return dag.container().from_("alpine:latest").with_file(f"/src/{name}", f)
import { dag, Container, File, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Return a container with a specified file
*/
@func()
async copyFile(
/**
* Source file
*/
f: File,
): Promise<Container> {
const name = await f.name()
return dag.container().from("alpine:latest").withFile(`/src/${name}`, f)
}
}
Example
-
Copy the
/home/admin/archives.zip
file on the host to the/src
directory in the container and return the modified container:dagger call copy-file --f=/home/admin/archives.zip
-
Copy the
/home/admin/archives.zip
file on the host to the/src
directory in the container and list the contents of the directory:dagger call copy-file --f=/home/admin/archives.zip directory --path=/src entries
Copy a subset of a directory or remote repository to a container using filters specified at run-time
The following Dagger Function accepts a Directory
argument, which could reference either a directory from the local filesystem or a remote Git repository. It copies the specified directory to the /src
path in a container, using filter patterns specified at call-time to exclude specified sub-directories and files, and returns the modified container.
When working with private Git repositories, ensure that SSH authentication is properly configured on your Dagger host.
This is an example of post-call filtering with directory filters.
- Go
- Python
- TypeScript
package main
import (
"context"
"dagger/my-module/internal/dagger"
)
type MyModule struct{}
// Return a container with a filtered directory
func (m *MyModule) CopyDirectoryWithExclusions(
ctx context.Context,
// Source directory
source *dagger.Directory,
// Directory exclusion pattern
// +optional
excludeDirectoryPattern string,
// +optional
// File exclusion pattern
excludeFilePattern string,
) *dagger.Container {
filteredSource := source.
WithoutDirectory(excludeDirectoryPattern).
WithoutFile(excludeFilePattern)
return dag.Container().
From("alpine:latest").
WithDirectory("/src", filteredSource)
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
@function
def copy_directory_with_exclusions(
self,
source: Annotated[dagger.Directory, Doc("Source directory")],
exclude_directory: Annotated[str, Doc("Directory exclusion pattern")] | None,
exclude_file: Annotated[str, Doc("File exclusion pattern")] | None,
) -> dagger.Container:
"""Return a container with a filtered directory"""
filtered_source = source
if exclude_directory is not None:
filtered_source = filtered_source.without_directory(exclude_directory)
if exclude_file is not None:
filtered_source = filtered_source.without_file(exclude_file)
return (
dag.container()
.from_("alpine:latest")
.with_directory("/src", filtered_source)
)
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Return a container with a filtered directory
*/
@func()
copyDirectoryWithExclusions(
/**
* Source directory
*/
source: Directory,
/**
* Directory exclusion pattern
*/
excludeDirectory?: string,
/**
* File exclusion pattern
*/
excludeFile?: string,
): Container {
let filteredSource = source
if (!excludeDirectory) {
filteredSource = filteredSource.withoutDirectory(excludeDirectory)
}
if (!excludeFile) {
filteredSource = filteredSource.withoutFile(excludeFile)
}
return dag
.container()
.from("alpine:latest")
.withDirectory("/src", filteredSource)
}
}
Examples
-
Copy the current host directory to
/src
in the container, excluding thedagger
sub-directory and thedagger.json
file, and return the modified container:dagger call copy-directory-with-exclusions --source=. --exclude-directory=dagger --exclude-file=dagger.json
-
Copy the public
dagger/dagger
GitHub repository to/src
in the container, excluding all Markdown files, and list the contents of the directory:dagger call copy-directory-with-exclusions --source=https://github.com/dagger/dagger#main --exclude-file='*.md' directory --path=/src entries
-
Copy the private
user/foo
GitHub repository to/src
in the container, excluding all Markdown files, and list the contents of the directory:dagger call copy-directory-with-exclusions --source=ssh://git@github.com/user/foo#main --exclude-file='*.md' directory --path=/src entries
Copy a file to the Dagger module runtime container for custom processing
The following Dagger Function accepts a File
argument and copies the specified file to the Dagger module runtime container. This makes it possible to add one or more files to the runtime container and manipulate them using custom logic.
- Go
- Python
- TypeScript
package main
import (
"context"
"dagger/my-module/internal/dagger"
"fmt"
"os"
)
type MyModule struct{}
// Copy a file to the Dagger module runtime container for custom processing
func (m *MyModule) CopyFile(ctx context.Context, source *dagger.File) {
source.Export(ctx, "foo.txt")
// your custom logic here
// for example, read and print the file in the Dagger Engine container
fmt.Println(os.ReadFile("foo.txt"))
}
import anyio
import dagger
from dagger import function, object_type
@object_type
class MyModule:
@function
async def copy_file(self, source: dagger.File):
"""Copy a file to the Dagger module runtime container for custom processing"""
await source.export("foo.txt")
# your custom logic here
# for example, read and print the file in the Dagger Engine container
print(await anyio.Path("foo.txt").read_text())
import { object, func, File } from "@dagger.io/dagger"
import * as fs from "fs"
@object()
class MyModule {
// Copy a file to the Dagger module runtime container for custom processing
@func()
async copyFile(source: File) {
await source.export("foo.txt")
// your custom logic here
// for example, read and print the file in the Dagger Engine container
console.log(fs.readFileSync("foo.txt", "utf8"))
}
}
Example
Copy the data.json
host file to the runtime container and process it:
dagger call copy-file --source=../data.json
Copy a subset of a directory or remote repository to a container using pre-defined filters
The following Dagger Function accepts a Directory
argument, which could reference either a directory from the local filesystem or a remote Git repository. It copies the specified directory to the /src
path in a container, using pre-defined filter patterns to exclude specified sub-directories and files, and returns the modified container.
When working with private Git repositories, ensure that SSH authentication is properly configured on your Dagger host.
This is an example of pre-call filtering with directory filters.
- Go
- Python
- TypeScript
package main
import (
"context"
"main/internal/dagger"
)
type MyModule struct{}
func (m *MyModule) CopyDirectoryWithExclusions(
ctx context.Context,
// +ignore=["*", "!**/*.md"]
source *dagger.Directory,
) (*dagger.Container, error) {
return dag.
Container().
From("alpine:latest").
WithDirectory("/src", source).
Sync(ctx)
}
from typing import Annotated
import dagger
from dagger import Ignore, dag, function, object_type
@object_type
class MyModule:
@function
async def copy_directory_with_exclusions(
self,
source: Annotated[dagger.Directory, Ignore(["*", "!*.md"])],
) -> dagger.Container:
return await (
dag.container().from_("alpine:latest").with_directory("/src", source).sync()
)
import {
dag,
object,
argument,
func,
Directory,
Container,
} from "@dagger.io/dagger"
@object()
class MyModule {
@func()
async copy_directory_with_exclusions(
@argument({ ignore: ["*", "!**/*.md"] }) source: Directory,
): Promise<Container> {
return await dag
.container()
.from("alpine:latest")
.withDirectory("/src", source)
.sync()
}
}
Examples
-
Copy the specified host directory to
/src
in the container, excluding everything except Markdown files, and return the modified container:dagger call copy-directory-with-exclusions --source=../docs
-
Copy the public
dagger/dagger
GitHub repository to/src
in the container, excluding everything except Markdown files, and list the contents of the/src
directory in the container:dagger call copy-directory-with-exclusions --source=https://github.com/dagger/dagger#main directory --path=/src entries
Builds
Perform a multi-stage build
The following Dagger Function performs a multi-stage build.
- Go
- Python
- TypeScript
package main
import (
"context"
"dagger/my-module/internal/dagger"
)
type MyModule struct{}
// Build and publish Docker container
func (m *MyModule) Build(
ctx context.Context,
// source code location
// can be local directory or remote Git repository
src *dagger.Directory,
) (string, error) {
// build app
builder := dag.Container().
From("golang:latest").
WithDirectory("/src", src).
WithWorkdir("/src").
WithEnvVariable("CGO_ENABLED", "0").
WithExec([]string{"go", "build", "-o", "myapp"})
// publish binary on alpine base
prodImage := dag.Container().
From("alpine").
WithFile("/bin/myapp", builder.File("/src/myapp")).
WithEntrypoint([]string{"/bin/myapp"})
// publish to ttl.sh registry
addr, err := prodImage.Publish(ctx, "ttl.sh/myapp:latest")
if err != nil {
return "", err
}
return addr, nil
}
import dagger
from dagger import dag, function, object_type
@object_type
class MyModule:
@function
def build(self, src: dagger.Directory) -> str:
"""Build and publish Docker container"""
# build app
builder = (
dag.container()
.from_("golang:latest")
.with_directory("/src", src)
.with_workdir("/src")
.with_env_variable("CGO_ENABLED", "0")
.with_exec(["go", "build", "-o", "myapp"])
)
# publish binary on alpine base
prod_image = (
dag.container()
.from_("alpine")
.with_file("/bin/myapp", builder.file("/src/myapp"))
.with_entrypoint(["/bin/myapp"])
)
# publish to ttl.sh registry
addr = prod_image.publish("ttl.sh/myapp:latest")
return addr
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Build and publish Docker container
*/
@func()
build(src: Directory): Promise<string> {
// build app
const builder = dag
.container()
.from("golang:latest")
.withDirectory("/src", src)
.withWorkdir("/src")
.withEnvVariable("CGO_ENABLED", "0")
.withExec(["go", "build", "-o", "myapp"])
// publish binary on alpine base
const prodImage = dag
.container()
.from("alpine")
.withFile("/bin/myapp", builder.file("/src/myapp"))
.withEntrypoint(["/bin/myapp"])
// publish to ttl.sh registry
const addr = prodImage.publish("ttl.sh/myapp:latest")
return addr
}
}
Example
Perform a multi-stage build of the source code in the golang/example/hello
repository and publish the resulting image:
dagger call build --src="https://github.com/golang/example#master:hello"
Perform a matrix build
The following Dagger Function performs a matrix build.
- Go
- Python
- TypeScript
package main
import (
"context"
"dagger/my-module/internal/dagger"
"fmt"
)
type MyModule struct{}
// Build and return directory of go binaries
func (m *MyModule) Build(
ctx context.Context,
// Source code location
src *dagger.Directory,
) *dagger.Directory {
// define build matrix
gooses := []string{"linux", "darwin"}
goarches := []string{"amd64", "arm64"}
// create empty directory to put build artifacts
outputs := dag.Directory()
golang := dag.Container().
From("golang:latest").
WithDirectory("/src", src).
WithWorkdir("/src")
for _, goos := range gooses {
for _, goarch := range goarches {
// create directory for each OS and architecture
path := fmt.Sprintf("build/%s/%s/", goos, goarch)
// build artifact
build := golang.
WithEnvVariable("GOOS", goos).
WithEnvVariable("GOARCH", goarch).
WithExec([]string{"go", "build", "-o", path})
// add build to outputs
outputs = outputs.WithDirectory(path, build.Directory(path))
}
}
// return build directory
return outputs
}
import dagger
from dagger import dag, function, object_type
@object_type
class MyModule:
@function
async def build(self, src: dagger.Directory) -> dagger.Directory:
"""Build and return directory of go binaries"""
# define build matrix
gooses = ["linux", "darwin"]
goarches = ["amd64", "arm64"]
# create empty directory to put build artifacts
outputs = dag.directory()
golang = (
dag.container()
.from_("golang:latest")
.with_directory("/src", src)
.with_workdir("/src")
)
for goos in gooses:
for goarch in goarches:
# create directory for each OS and architecture
path = f"build/{goos}/{goarch}/"
# build artifact
build = (
golang.with_env_variable("GOOS", goos)
.with_env_variable("GOARCH", goarch)
.with_exec(["go", "build", "-o", path])
)
# add build to outputs
outputs = outputs.with_directory(path, build.directory(path))
return await outputs
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Build and return directory of go binaries
*/
@func()
build(src: Directory): Directory {
// define build matrix
const gooses = ["linux", "darwin"]
const goarches = ["amd64", "arm64"]
// create empty directory to put build artifacts
let outputs = dag.directory()
const golang = dag
.container()
.from("golang:latest")
.withDirectory("/src", src)
.withWorkdir("/src")
for (const goos of gooses) {
for (const goarch of goarches) {
// create a directory for each OS and architecture
const path = `build/${goos}/${goarch}/`
// build artifact
const build = golang
.withEnvVariable("GOOS", goos)
.withEnvVariable("GOARCH", goarch)
.withExec(["go", "build", "-o", path])
// add build to outputs
outputs = outputs.withDirectory(path, build.directory(path))
}
}
return outputs
}
}
Example
Perform a matrix build of the source code in the golang/example/hello
repository and export build directory with go binaries for different operating systems and architectures.
dagger call build \
--src="https://github.com/golang/example#master:hello" \
export --path /tmp/matrix-builds
Inspect the contents of the exported directory with tree /tmp/matrix-builds
. The output should look like this:
/tmp/matrix-builds
└── build
├── darwin
│ ├── amd64
│ │ └── hello
│ └── arm64
│ └── hello
└── linux
├── amd64
│ └── hello
└── arm64
└── hello
8 directories, 4 files
Build multi-arch image
The following Dagger Function builds a single image for different CPU architectures using native emulation.
- Go
- Python
- TypeScript
package main
import (
"context"
"dagger/my-module/internal/dagger"
)
type MyModule struct{}
// Build and publish multi-platform image
func (m *MyModule) Build(
ctx context.Context,
// Source code location
// can be local directory or remote Git repository
src *dagger.Directory,
) (string, error) {
// 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
}
// container registry for the multi-platform image
const imageRepo = "ttl.sh/myapp:latest"
platformVariants := make([]*dagger.Container, 0, len(platforms))
for _, platform := range platforms {
// pull golang image for this platform
ctr := dag.Container(dagger.ContainerOpts{Platform: platform}).
From("golang:1.20-alpine").
// mount source code
WithDirectory("/src", src).
// mount empty dir where built binary will live
WithDirectory("/output", dag.Directory()).
// ensure binary will be statically linked and thus executable
// in the final image
WithEnvVariable("CGO_ENABLED", "0").
// build binary and put result at mounted output directory
WithWorkdir("/src").
WithExec([]string{"go", "build", "-o", "/output/hello"})
// select output directory
outputDir := ctr.Directory("/output")
// wrap the output directory in the new empty container marked
// with the same platform
binaryCtr := dag.Container(dagger.ContainerOpts{Platform: platform}).
WithRootfs(outputDir)
platformVariants = append(platformVariants, binaryCtr)
}
// publish to registry
imageDigest, err := dag.Container().
Publish(ctx, imageRepo, dagger.ContainerPublishOpts{
PlatformVariants: platformVariants,
})
if err != nil {
return "", err
}
// return build directory
return imageDigest, nil
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
@function
async def build(
self,
src: Annotated[
dagger.Directory,
Doc(
"Source code location can be local directory or remote Git \
repository"
),
],
) -> str:
"""Build and publish multi-platform image"""
# platforms to build for and push in a multi-platform image
platforms = [
dagger.Platform("linux/amd64"), # a.k.a. x86_64
dagger.Platform("linux/arm64"), # a.k.a. aarch64
dagger.Platform("linux/s390x"), # a.k.a. IBM S/390
]
# container registry for multi-platform image
image_repo = "ttl.sh/myapp:latest"
platform_variants = []
for platform in platforms:
# pull golang image for this platform
ctr = (
dag.container(platform=platform)
.from_("golang:1.20-alpine")
# mount source
.with_directory("/src", src)
# mount empty dir where built binary will live
.with_directory("/output", dag.directory())
# ensure binary will be statically linked and thus executable
# in the final image
.with_env_variable("CGO_ENABLED", "0")
# build binary and put result at mounted output directory
.with_workdir("/src")
.with_exec(["go", "build", "-o", "/output/hello"])
)
# select output directory
output_dir = ctr.directory("/output")
# wrap output directory in a new empty container marked
# with the same platform
binary_ctr = dag.container(platform=platform).with_rootfs(output_dir)
platform_variants.append(binary_ctr)
# publish to registry
image_digest = dag.container().publish(
image_repo, platform_variants=platform_variants
)
return await image_digest
import {
dag,
Container,
Directory,
Platform,
object,
func,
} from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Build and publish multi-platform image
* @param src source code location
*/
@func()
async build(src: Directory): Promise<string> {
// platforms to build for and push in a multi-platform image
const platforms: Platform[] = [
"linux/amd64" as Platform, // a.k.a. x86_64
"linux/arm64" as Platform, // a.k.a. aarch64
"linux/s390x" as Platform, // a.k.a. IBM S/390
]
// container registry for multi-platform image
const imageRepo = "ttl.sh/myapp:latest"
const platformVariants: Array<Container> = []
for (const platform of platforms) {
const ctr = dag
.container({ platform: platform })
.from("golang:1.21-alpine")
// mount source
.withDirectory("/src", src)
// mount empty dir where built binary will live
.withDirectory("/output", dag.directory())
// ensure binary will be statically linked and thus executable
// in the final image
.withEnvVariable("CGO_ENABLED", "0")
.withWorkdir("/src")
.withExec(["go", "build", "-o", "/output/hello"])
// select output directory
const outputDir = ctr.directory("/output")
// wrap output directory in a new empty container marked
// with the same platform
const binaryCtr = await dag
.container({ platform: platform })
.withRootfs(outputDir)
platformVariants.push(binaryCtr)
}
// publish to registry
const imageDigest = await dag
.container()
.publish(imageRepo, { platformVariants: platformVariants })
return imageDigest
}
}
Example
Build and publish a multi-platform image:
dagger call build --src="https://github.com/golang/example#master:hello"
Build multi-arch image with cross-compliation
The following Dagger Function builds a single image for different CPU architectures using cross-compilation.
This Dagger Function uses the containerd
utility module. To run it locally
install the module first with dagger install github.com/levlaz/daggerverse/containerd@v0.1.2
- Go
- Python
- TypeScript
package main
import (
"context"
"dagger/my-module/internal/dagger"
)
type MyModule struct{}
// Build and publish multi-platform image
func (m *MyModule) Build(
ctx context.Context,
// Source code location
// can be local directory or remote Git repository
src *dagger.Directory,
) (string, error) {
// 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
}
// container registry for the multi-platform image
const imageRepo = "ttl.sh/myapp:latest"
platformVariants := make([]*dagger.Container, 0, len(platforms))
for _, platform := range platforms {
// parse architecture using containerd utility module
platformArch, err := dag.Containerd().ArchitectureOf(ctx, platform)
if err != nil {
return "", err
}
// pull golang image for the *host* platform, this is done by
// not specifying the a platform. The default is the host platform.
ctr := dag.Container().
From("golang:1.21-alpine").
// mount source code
WithDirectory("/src", src).
// mount empty dir where built binary will live
WithDirectory("/output", dag.Directory()).
// ensure binary will be statically linked and thus executable
// in the final image
WithEnvVariable("CGO_ENABLED", "0").
// configure go compiler to use cross-compilation targeting the
// desired platform
WithEnvVariable("GOOS", "linux").
WithEnvVariable("GOARCH", platformArch).
// build binary and put result at mounted output directory
WithWorkdir("/src").
WithExec([]string{"go", "build", "-o", "/output/hello"})
// select output directory
outputDir := ctr.Directory("/output")
// wrap the output directory in the new empty container marked
// with the same platform
binaryCtr := dag.Container(dagger.ContainerOpts{Platform: platform}).
WithRootfs(outputDir).
WithEntrypoint([]string{"/hello"})
platformVariants = append(platformVariants, binaryCtr)
}
// publish to registry
imageDigest, err := dag.Container().
Publish(ctx, imageRepo, dagger.ContainerPublishOpts{
PlatformVariants: platformVariants,
})
if err != nil {
return "", err
}
// return build directory
return imageDigest, nil
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
@function
async def build(
self,
src: Annotated[
dagger.Directory,
Doc(
"Source code location can be local directory or remote Git \
repository"
),
],
) -> str:
"""Build an publish multi-platform image"""
# platforms to build for and push in a multi-platform image
platforms = [
dagger.Platform("linux/amd64"), # a.k.a. x86_64
dagger.Platform("linux/arm64"), # a.k.a. aarch64
dagger.Platform("linux/s390x"), # a.k.a. IBM S/390
]
# container registry for multi-platform image
image_repo = "ttl.sh/myapp:latest"
platform_variants = []
for platform in platforms:
# parse architecture using containerd utility module
platform_arch = await dag.containerd().architecture_of(platform)
# pull golang image for the *host* platform, this is done by
# not specifying the a platform. The default is the host platform.
ctr = (
dag.container()
.from_("golang:1.21-alpine")
# mount source
.with_directory("/src", src)
# mount empty dir where built binary will live
.with_directory("/output", dag.directory())
# ensure binary will be statically linked and thus executable
# in the final image
.with_env_variable("CGO_ENABLED", "0")
# configure go compiler to use cross-compilation targeting the
# desired platform
.with_env_variable("GOOS", "linux")
.with_env_variable("GOARCH", platform_arch)
# build binary and put result at mounted output directory
.with_workdir("/src")
.with_exec(["go", "build", "-o", "/output/hello"])
)
# selelct output directory
output_dir = ctr.directory("/output")
# wrap output directory in a new empty container marked
# with the same platform
binary_ctr = (
dag.container(platform=platform)
.with_rootfs(output_dir)
.with_entrypoint(["/hello"])
)
platform_variants.append(binary_ctr)
# publish to registry
image_digest = dag.container().publish(
image_repo, platform_variants=platform_variants
)
return await image_digest
import {
dag,
Container,
Directory,
Platform,
object,
func,
} from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Build and publish multi-platform image
* @param src source code location
*/
@func()
async build(src: Directory): Promise<string> {
// platforms to build for and push in a multi-platform image
const platforms: Platform[] = [
"linux/amd64" as Platform, // a.k.a. x86_64
"linux/arm64" as Platform, // a.k.a. aarch64
"linux/s390x" as Platform, // a.k.a. IBM S/390
]
// container registry for multi-platform image
const imageRepo = "ttl.sh/myapp:latest"
const platformVariants: Array<Container> = []
for (const platform of platforms) {
// parse architecture using containerd utility module
const platformArch = await dag.containerd().architectureOf(platform)
const ctr = dag
// pull golang image for the *host* platform, this is done by
// not specifying the a platform. The default is the host platform.
.container()
.from("golang:1.21-alpine")
// mount source
.withDirectory("/src", src)
// mount empty dir where built binary will live
.withDirectory("/output", dag.directory())
// ensure binary will be statically linked and thus executable
// in the final image
.withEnvVariable("CGO_ENABLED", "0")
// configure go compiler to use cross-compilation targeting the
// desired platform
.withEnvVariable("GOOS", "linux")
.withEnvVariable("GOARCH", platformArch)
.withWorkdir("/src")
.withExec(["go", "build", "-o", "/output/hello"])
// select output directory
const outputDir = ctr.directory("/output")
// wrap output directory in a new empty container marked
// with the same platform
const binaryCtr = await dag
.container({ platform: platform })
.withRootfs(outputDir)
.withEntrypoint(["/hello"])
platformVariants.push(binaryCtr)
}
// publish to registry
const imageDigest = await dag
.container()
.publish(imageRepo, { platformVariants: platformVariants })
return imageDigest
}
}
Example
Build and publish a multi-platform image with cross compliation:
dagger call build --src="https://github.com/golang/example#master:hello"
Build image from Dockerfile
The following Dagger Function builds an image from a Dockerfile.
- Go
- Python
- TypeScript
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 := dag.Container().
WithDirectory("/src", src).
WithWorkdir("/src").
Directory("/src").
DockerBuild(). // build from Dockerfile
Publish(ctx, "ttl.sh/hello-dagger")
if err != nil {
return "", err
}
return ref, nil
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
@function
async def build(
self,
src: Annotated[
dagger.Directory,
Doc("location of directory containing Dockerfile"),
],
) -> str:
"""Build and publish image from existing Dockerfile"""
ref = (
dag.container()
.with_directory("/src", src)
.with_workdir("/src")
.directory("/src")
.docker_build() # build from Dockerfile
.publish("ttl.sh/hello-dagger")
)
return await ref
import { dag, Directory, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Build and publish image from existing Dockerfile
* @param src location of directory containing Dockerfile
*/
@func()
async build(src: Directory): Promise<string> {
const ref = await dag
.container()
.withDirectory("/src", src)
.withWorkdir("/src")
.directory("/src")
.dockerBuild() // build from Dockerfile
.publish("ttl.sh/hello-dagger")
return ref
}
}
Example
Build and publish an image from an existing Dockerfile
dagger call build --src 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.
- Go
- Python
- TypeScript
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
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
@function
async def build(
self,
src: Annotated[
dagger.Directory,
Doc("location of source directory"),
],
dockerfile: Annotated[
dagger.File,
Doc("location of Dockerfile"),
],
) -> str:
"""
Build and publish image from Dockerfile
This example uses a build context directory in a different location
than the current working directory.
"""
# get build context with dockerfile added
workspace = (
dag.container()
.with_directory("/src", src)
.with_workdir("/src")
.with_file("/src/custom.Dockerfile", dockerfile)
.directory("/src")
)
# build using Dockerfile and publish to registry
ref = (
dag.container()
.build(context=workspace, dockerfile="custom.Dockerfile")
.publish("ttl.sh/hello-dagger")
)
return await ref
import { dag, Directory, File, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Build and publish image from existing Dockerfile. This example uses a
* build context directory in a different location than the current working
* directory.
* @param src location of source directory
* @param dockerfile location of dockerfile
*/
@func()
async build(src: Directory, dockerfile: File): Promise<string> {
// get build context with Dockerfile added
const workspace = await dag
.container()
.withDirectory("/src", src)
.withWorkdir("/src")
.withFile("/src/custom.Dockerfile", dockerfile)
.directory("/src")
// build using Dockerfile and publish to registry
const ref = await dag
.container()
.build(workspace, { dockerfile: "custom.Dockerfile" })
.publish("ttl.sh/hello-dagger")
return ref
}
}
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 call build \
--src "https://github.com/dockersamples/python-flask-redis" \
--dockerfile https://github.com/vimagick/dockerfiles#master:registry-cli/Dockerfile
Add OCI labels to image
The following Dagger Function adds OpenContainer Initiative (OCI) labels to an image.
- Go
- Python
- TypeScript
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
}
from datetime import datetime, timezone
from dagger import dag, function, object_type
@object_type
class MyModule:
@function
async def build(self) -> str:
"""Build and publish image with oci labels"""
ref = (
dag.container()
.from_("alpine")
.with_label("org.opencontainers.image.title", "my-alpine")
.with_label("org.opencontainers.image.version", "1.0")
.with_label(
"org.opencontainers.image.created",
datetime.now(timezone.utc).isoformat(),
)
.with_label(
"org.opencontainers.image.source",
"https://github.com/alpinelinux/docker-alpine",
)
.with_label("org.opencontainers.image.licenses", "MIT")
.publish("ttl.sh/my-alpine")
)
return await ref
import { dag, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
/**
* Build and publish image with oci labels
*/
@func()
async build(): Promise<string> {
const ref = await dag
.container()
.from("alpine")
.withLabel("org.opencontainers.image.title", "my-alpine")
.withLabel("org.opencontainers.image.version", "1.0")
.withLabel("org.opencontainers.image.created", new Date())
.withLabel(
"org.opencontainers.image.source",
"https://github.com/alpinelinux/docker-alpine",
)
.withLabel("org.opencontainers.image.licenses", "MIT")
.publish("ttl.sh/hello-dagger")
return ref
}
}