# Dagger

> 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. USE FOR: CI/CD pipelines as code, Dagger Functions and modules, container-based build steps, reproducible pipelines DO NOT USE FOR: cloud infrastructure provisioning (use terraform or pulumi), container image authoring (use docker), Kubernetes deployment (use kubernetes)

- Skill: `tyler-r-kendrick/dagger` (Agent Skill, multi-file: 12 files)
- Install (CLI): `npx skillmds add tyler-r-kendrick/dagger`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tyler-r-kendrick/dagger/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: Tyler-R-Kendrick (https://skillmd.com/u/tyler-r-kendrick)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/tyler-r-kendrick/dagger

---


# 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
```typescript
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
```python
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
```bash
# 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
```bash
# 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.

