Dagger
Overview
Dagger is an open-source runtime for composable software workflows. It lets you write CI/CD pipelines in real programming languages (TypeScript, Python, Go) that run in containers, providing automatic caching, reproducibility, and portability across local dev and CI environments.
Core Concepts
- Functions — typed, cacheable units of work that run in containers
- Modules — collections of related functions, shareable and composable
- Dagger Engine — runs functions in containers with automatic caching
TypeScript Example
import { dag, Container, Directory, object, func } from "@dagger.io/dagger";
@object()
class MyPipeline {
@func()
async build(source: Directory): Promise<Container> {
return dag
.container()
.from("node:22")
.withDirectory("/app", source)
.withWorkdir("/app")
.withExec(["npm", "ci"])
.withExec(["npm", "run", "build"]);
}
@func()
async test(source: Directory): Promise<string> {
return this.build(source)
.then(ctr => ctr.withExec(["npm", "test"]).stdout());
}
@func()
async publish(source: Directory, tag: string): Promise<string> {
const built = await this.build(source);
return built
.withEntrypoint(["node", "dist/index.js"])
.publish(`ttl.sh/my-app:${tag}`);
}
}
Python Example
import dagger
from dagger import dag, function, object_type
@object_type
class MyPipeline:
@function
async def build(self, source: dagger.Directory) -> dagger.Container:
return (
dag.container()
.from_("python:3.12")
.with_directory("/app", source)
.with_workdir("/app")
.with_exec(["pip", "install", "-r", "requirements.txt"])
)
@function
async def test(self, source: dagger.Directory) -> str:
return await (
(await self.build(source))
.with_exec(["pytest"])
.stdout()
)
CLI Usage
# Initialize a new Dagger module
dagger init --sdk=typescript
# Call a function
dagger call build --source=.
# Run tests
dagger call test --source=.
# Publish a container
dagger call publish --source=. --tag=latest
Key Features
| Feature |
Description |
| Automatic caching |
Every function result is cached by inputs; unchanged steps are skipped |
| Container isolation |
Functions run in containers, ensuring reproducibility |
| Type safety |
Full type checking from your language's SDK |
| Composability |
Call functions from other modules |
| Local + CI parity |
Same pipeline runs identically on your laptop and in CI |
Using Existing Modules
# Use a community module
dagger call -m github.com/dagger/dagger/modules/wolfi container --packages=curl,git
Best Practices
- Write functions as small, composable units — each function should do one thing.
- Use
withDirectory to pass source code into containers rather than bind-mounting.
- Leverage automatic caching — structure functions so unchanged inputs skip work.
- Use
dagger call locally to test pipelines before pushing to CI.
- Use Dagger modules from the community instead of reimplementing common tasks (linting, testing, publishing).
- Keep secrets out of function arguments — use Dagger's
Secret type for sensitive values.
1---2name: dagger3description: |4 Use when building CI/CD pipelines as code with Dagger. Covers Dagger Functions, modules, container-based execution, caching, and SDK usage in TypeScript, Python, and Go.5 USE FOR: CI/CD pipelines as code, Dagger Functions and modules, container-based build steps, reproducible pipelines6 DO NOT USE FOR: cloud infrastructure provisioning (use terraform or pulumi), container image authoring (use docker), Kubernetes deployment (use kubernetes)7license: MIT8metadata:9 displayName: "Dagger"10 author: "Tyler-R-Kendrick"11compatibility: claude, copilot, cursor12references:13 - title: "Dagger Documentation"14 url: "https://docs.dagger.io"15 - title: "Dagger GitHub Repository"16 url: "https://github.com/dagger/dagger"17---1819# Dagger2021## Overview22Dagger is an open-source runtime for composable software workflows. It lets you write CI/CD pipelines in real programming languages (TypeScript, Python, Go) that run in containers, providing automatic caching, reproducibility, and portability across local dev and CI environments.2324## Core Concepts25- **Functions** — typed, cacheable units of work that run in containers26- **Modules** — collections of related functions, shareable and composable27- **Dagger Engine** — runs functions in containers with automatic caching2829## TypeScript Example30```typescript31import { dag, Container, Directory, object, func } from "@dagger.io/dagger";3233@object()34class MyPipeline {35 @func()36 async build(source: Directory): Promise<Container> {37 return dag38 .container()39 .from("node:22")40 .withDirectory("/app", source)41 .withWorkdir("/app")42 .withExec(["npm", "ci"])43 .withExec(["npm", "run", "build"]);44 }4546 @func()47 async test(source: Directory): Promise<string> {48 return this.build(source)49 .then(ctr => ctr.withExec(["npm", "test"]).stdout());50 }5152 @func()53 async publish(source: Directory, tag: string): Promise<string> {54 const built = await this.build(source);55 return built56 .withEntrypoint(["node", "dist/index.js"])57 .publish(`ttl.sh/my-app:${tag}`);58 }59}60```6162## Python Example63```python64import dagger65from dagger import dag, function, object_type6667@object_type68class MyPipeline:69 @function70 async def build(self, source: dagger.Directory) -> dagger.Container:71 return (72 dag.container()73 .from_("python:3.12")74 .with_directory("/app", source)75 .with_workdir("/app")76 .with_exec(["pip", "install", "-r", "requirements.txt"])77 )7879 @function80 async def test(self, source: dagger.Directory) -> str:81 return await (82 (await self.build(source))83 .with_exec(["pytest"])84 .stdout()85 )86```8788## CLI Usage89```bash90# Initialize a new Dagger module91dagger init --sdk=typescript9293# Call a function94dagger call build --source=.9596# Run tests97dagger call test --source=.9899# Publish a container100dagger call publish --source=. --tag=latest101```102103## Key Features104| Feature | Description |105|---------|-------------|106| **Automatic caching** | Every function result is cached by inputs; unchanged steps are skipped |107| **Container isolation** | Functions run in containers, ensuring reproducibility |108| **Type safety** | Full type checking from your language's SDK |109| **Composability** | Call functions from other modules |110| **Local + CI parity** | Same pipeline runs identically on your laptop and in CI |111112## Using Existing Modules113```bash114# Use a community module115dagger call -m github.com/dagger/dagger/modules/wolfi container --packages=curl,git116```117118## Best Practices119- Write functions as small, composable units — each function should do one thing.120- Use `withDirectory` to pass source code into containers rather than bind-mounting.121- Leverage automatic caching — structure functions so unchanged inputs skip work.122- Use `dagger call` locally to test pipelines before pushing to CI.123- Use Dagger modules from the community instead of reimplementing common tasks (linting, testing, publishing).124- Keep secrets out of function arguments — use Dagger's `Secret` type for sensitive values.