TL;DR
Dagger lets you write CI/CD pipelines as code in TypeScript, Python, or Go — then run them anywhere: locally, in GitHub Actions, GitLab CI, or any container runtime. No more YAML.
What Is Dagger?
Dagger replaces CI/CD YAML with real code:
- Code, not YAML — TypeScript, Python, Go SDKs
- Run anywhere — local, GitHub Actions, GitLab CI, Jenkins
- Container-native — every step runs in a container
- Caching — automatic layer caching for fast builds
- Portable — same pipeline works on every CI platform
- Local debugging — run your CI pipeline on your laptop
- Free — Apache 2.0
Quick Start
# Install Dagger CLI
curl -fsSL https://dl.dagger.io/dagger/install.sh | sh
# Initialize a Dagger module
dagger init --sdk=typescript
TypeScript Pipeline
// dagger/src/index.ts
import { dag, Container, Directory, object, func } from "@dagger.io/dagger";
@object()
class MyPipeline {
@func()
async test(source: Directory): Promise<string> {
return dag
.container()
.from("node:20")
.withDirectory("/app", source)
.withWorkdir("/app")
.withExec(["npm", "install"])
.withExec(["npm", "test"])
.stdout();
}
@func()
async build(source: Directory): Promise<Container> {
return dag
.container()
.from("node:20-alpine")
.withDirectory("/app", source)
.withWorkdir("/app")
.withExec(["npm", "install", "--production"])
.withExec(["npm", "run", "build"])
.withEntrypoint(["node", "dist/index.js"]);
}
@func()
async publish(source: Directory, registry: string, tag: string): Promise<string> {
const container = await this.build(source);
return container.publish(`${registry}:${tag}`);
}
@func()
async ci(source: Directory): Promise<string> {
// Run tests
await this.test(source);
// Build and publish
const image = await this.publish(source, "ghcr.io/myorg/myapp", "latest");
return `Published: ${image}`;
}
}
Run Locally
# Run tests locally (same as CI!)
dagger call test --source=.
# Build container
dagger call build --source=.
# Full CI pipeline
dagger call ci --source=.
Use in GitHub Actions
# .github/workflows/ci.yml
name: CI
on: [push]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dagger/dagger-for-github@v6
with:
verb: call
args: ci --source=.
Multi-Stage with Caching
@func()
async buildOptimized(source: Directory): Promise<Container> {
// Install deps with cache
const deps = dag
.container()
.from("node:20")
.withDirectory("/app", source, { include: ["package*.json"] })
.withWorkdir("/app")
.withMountedCache("/root/.npm", dag.cacheVolume("npm"))
.withExec(["npm", "ci"]);
// Build with full source
const built = deps
.withDirectory("/app", source)
.withExec(["npm", "run", "build"]);
// Production image (small)
return dag
.container()
.from("node:20-alpine")
.withDirectory("/app/dist", built.directory("/app/dist"))
.withDirectory("/app/node_modules", deps.directory("/app/node_modules"))
.withWorkdir("/app")
.withEntrypoint(["node", "dist/index.js"]);
}
Dagger vs Alternatives
| Feature | Dagger | GitHub Actions | GitLab CI | Jenkins |
|---|---|---|---|---|
| Config | Code (TS/Py/Go) | YAML | YAML | Groovy/YAML |
| Local run | Yes | act (limited) | No | No |
| Portable | Any CI | GitHub only | GitLab only | Jenkins only |
| Caching | Automatic | Manual | Manual | Manual |
| Debugging | Full local | Commit-push-wait | Commit-push-wait | Console |
| Container-native | Yes | Yes | Yes | Plugin |
Resources
- Dagger Documentation
- GitHub Repository — 12K+ stars
- Examples
- Daggerverse — reusable modules
Building CI/CD for scraping pipelines? My Apify tools extract web data — automate deployments with Dagger for reproducible, testable pipelines. Questions? Email spinov001@gmail.com
Top comments (0)