Complete Guide to Dagger v0.20 — Beyond YAML Pipelines with CI/CD as Code: Dang Scripting, Daggerverse Modules, and Production Deployment Strategies
In 2026, CI/CD pipelines are still trapped in YAML hell. GitHub Actions' .github/workflows/*.yml, GitLab CI's .gitlab-ci.yml, Jenkins' Jenkinsfile — different syntax per platform, no local testing, and vendor lock-in. Dagger, created by Docker founder Solomon Hykes, is a container-native automation engine that lets you write CI/CD pipelines in programming languages and run them identically anywhere inside containers.
v0.20, released in February 2026, introduced the dedicated Dang scripting language, a completely redesigned terminal UI, and a logs-based progress mode. In OpenMeter's real-world case, a pipeline that took 25 minutes on GitHub Actions alone was reduced to 5 minutes (5x faster, 50% cost reduction) with Dagger Cloud caching. This guide covers Dagger's architecture, SDK ecosystem, Daggerverse modules, CI integration patterns, Kubernetes production deployment, and comparison with existing CI/CD tools.
Dagger Architecture — DAG-Based Container Execution Engine
Core Structure: Engine → GraphQL API → SDK
Understanding Dagger's architecture requires distinguishing three layers. The Dagger Engine is a core runtime combining an execution engine, universal type system, data layer, and module system. It runs on any OCI-compatible system. Each language SDK (Go, Python, TypeScript, etc.) doesn't execute pipelines directly — instead, it sends pipeline definitions to the Dagger GraphQL API, which triggers the Engine.
┌─────────────────────────────────────────────────────────────┐
│ Your Code (Go / Python / TypeScript / Dang) │
│ └── SDK: Converts pipeline definitions to GraphQL requests │
├─────────────────────────────────────────────────────────────┤
│ Dagger GraphQL API │
│ └── Parses pipeline definitions into a DAG │
├─────────────────────────────────────────────────────────────┤
│ Dagger Engine │
│ ├── Executes DAG operations concurrently │
│ ├── Intermediate artifacts: JIT build + incremental cache │
│ ├── Container sandbox: all ops run inside containers │
│ └── Auto-generates OpenTelemetry traces │
└─────────────────────────────────────────────────────────────┘
When the Engine receives an API request, it computes a DAG (Directed Acyclic Graph) of low-level operations and processes them concurrently. Intermediate artifacts are built just-in-time, and every operation is incremental by default. Once all operations resolve, the Engine returns results to your program.
CI-Agnostic Design — "Write Once, Run Anywhere"
Dagger's core design principle is CI-agnostic. Your pipelines aren't tied to any platform or provider. GitHub Actions, GitLab CI, Jenkins, CircleCI, Azure Pipelines, or your local terminal — the same pipeline logic works everywhere.
Key Value: This fundamentally solves "works on my machine" problems. A pipeline tested locally with
dagger call buildruns identically on CI runners. Local debugging — impossible with YAML-based CI — is now a reality.
v0.20 Key Features — Dang, Native TUI, Cache Management
Dang: Dagger's Dedicated Scripting Language
Introduced in v0.20.2, Dang is a scripting language designed specifically for Dagger. It runs on a native runtime built into the Engine and introspects the Engine schema at runtime, making every Dagger type a native language type. No codegen, near-instant startup, and concise syntax optimized for AI-assisted development.
# Build pipeline in Dang — much more concise than Go SDK
container |
from "golang:1.22-alpine" |
with-exec ["apk", "add", "git"] |
with-directory "/src" (host | directory ".") |
with-workdir "/src" |
with-exec ["go", "test", "./..."] |
with-exec ["go", "build", "-o", "/app", "."]
Dang modules run directly inside the Engine, eliminating the usual module loading overhead (container startup, process coordination). This is particularly valuable for workflows where AI agents generate and modify pipelines.
Redesigned Native TUI
v0.20.2 completely rebuilt the interactive TUI. Instead of the previous fixed viewport, it uses the terminal's native scrollback — free scrolling, link clicking, and text selection. A separate logs-based progress mode (--progress=logs) was also added for easier CI log scanning.
Improved Manual Cache Management
For teams with automatic GC disabled, explicit space threshold-based cache pruning was introduced. Per-call overrides are available for max used space, reserved space, minimum free space, and target space.
# Cache pruning options
dagger call build \
--cache-max-used-space=20GB \
--cache-reserved-space=5GB \
--cache-min-free-space=10GB
SDK Ecosystem — Write Pipelines in 8 Languages
Dagger auto-generates SDKs from its API schema, providing full type safety and editor support (autocomplete, linting) for each language.
| SDK | Maturity | Latest Version | Highlights |
|---|---|---|---|
| Go | GA | v0.20.3 | Most mature, official reference implementation |
| Python | GA | v0.20.3 | async/await, typing support |
| TypeScript | GA | v0.20.3 | Deno/Bun support, ESM default |
| Rust | GA | v0.20.3 | Ideal for system-level pipelines |
| PHP | Beta | - | Laravel/Symfony ecosystem integration |
| Java | Beta | - | For Maven/Gradle projects |
| .NET | Beta | - | C# pipeline authoring |
| Elixir | Beta | - | For Phoenix projects |
TypeScript SDK Example — Build → Test → Publish
// dagger/src/index.ts — Full-stack CI pipeline with TypeScript SDK
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"
@object()
class CiPipeline {
@func()
async build(source: Directory): Promise<Container> {
return dag
.container()
.from("node:22-alpine")
.withDirectory("/app", source)
.withWorkdir("/app")
.withExec(["npm", "ci"])
.withExec(["npm", "run", "build"])
}
@func()
async test(source: Directory): Promise<string> {
const ctr = await this.build(source)
return ctr
.withExec(["npm", "run", "test", "--", "--coverage"])
.stdout()
}
@func()
async publish(
source: Directory,
registry: string,
tag: string
): Promise<string> {
const built = await this.build(source)
return built
.withEntrypoint(["node", "dist/main.js"])
.publish(`${registry}:${tag}`)
}
}
# Run locally — identical results to CI
dagger call test --source=.
# Run in GitHub Actions — same command
dagger call publish --source=. --registry=ghcr.io/myorg/myapp --tag=v1.2.3
Go SDK Example — Multi-Architecture Build
// dagger/main.go — Multi-arch container build with Go SDK
package main
import (
"context"
"dagger/ci/internal/dagger"
)
type Ci struct{}
func (m *Ci) BuildMultiArch(
ctx context.Context,
source *dagger.Directory,
) (*dagger.Container, error) {
platforms := []dagger.Platform{
"linux/amd64",
"linux/arm64",
}
platformVariants := make([]*dagger.Container, len(platforms))
for i, platform := range platforms {
platformVariants[i] = dag.Container(dagger.ContainerOpts{
Platform: platform,
}).
From("golang:1.22-alpine").
WithDirectory("/src", source).
WithWorkdir("/src").
WithExec([]string{"go", "build", "-o", "/app", "."})
}
return dag.Container().
Publish(ctx, "ghcr.io/myorg/app:latest",
dagger.ContainerPublishOpts{
PlatformVariants: platformVariants,
})
}
Daggerverse — 1,500+ Reusable Modules
Daggerverse serves as the central index for Dagger modules, similar to DockerHub's role for container images. Over 1,500 public modules are registered, hosted on GitHub public repositories. Dagger indexes modules — it doesn't host the code.
| Category | Key Modules | Purpose |
|---|---|---|
| Build | golang, node, python, rust | Standardized language build steps |
| Test | pytest, vitest, go-test | Test runner integration |
| Security | trivy, grype, cosign | Image scanning + signing |
| Deploy | helm, kubectl, terraform | Infrastructure provisioning |
| AI | daggie, openai, ollama | AI agent pipelines |
| Notifications | slack, discord, github-comment | Pipeline result notifications |
# Using Daggerverse modules — direct remote module calls
dagger call -m github.com/purpleclay/daggerverse/trivy@v0.5.0 \
scan --source=. --severity=HIGH,CRITICAL
# Production tip: Always pin versions for remote modules
# ✅ github.com/user/module@v1.2.3
# ❌ github.com/user/module (latest auto — not recommended)
Production Warning: Always pin specific versions when using Daggerverse modules in production to prevent unexpected changes. Manage internal modules in a monorepo subdirectory.
CI Integration Patterns — GitHub Actions, GitLab CI, Jenkins
GitHub Actions + Dagger
Dagger replaces GitHub Actions' YAML definitions while keeping the runner infrastructure. YAML serves only as a minimal wrapper for Dagger calls.
# .github/workflows/ci.yml — Dagger + GitHub Actions
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
dagger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Dagger Pipeline
uses: dagger/dagger-for-github@v8
with:
version: "0.20.3"
verb: call
args: test --source=.
- name: Build and Push
if: github.ref == 'refs/heads/main'
uses: dagger/dagger-for-github@v8
with:
version: "0.20.3"
verb: call
args: publish --source=. --registry=ghcr.io/${{ github.repository }} --tag=${{ github.sha }}
GitLab CI + Dagger
# .gitlab-ci.yml — Dagger + GitLab CI
stages:
- ci
dagger:
stage: ci
image: alpine:latest
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- apk add --no-cache curl
- curl -fsSL https://dl.dagger.io/dagger/install.sh | sh
- export PATH=$HOME/.dagger/bin:$PATH
script:
- dagger call test --source=.
- dagger call publish --source=. --registry=$CI_REGISTRY_IMAGE --tag=$CI_COMMIT_SHA
Dagger Cloud Checks — Managed CI
Dagger Cloud connects to your Git provider and automatically runs dagger check on every change. Auto-scaled on cloud engines — no YAML, no vendor syntax, no orchestration layer. Through a partnership with Depot, managed Dagger Powered GitHub Actions runners are also available with pre-installed Dagger, automatic persistent layer caching, and multi-architecture support.
Kubernetes Production Deployment
DaemonSet Pattern
In Kubernetes, the Dagger Engine deploys as a DaemonSet — one Engine instance per node for maximum resource efficiency and local cache reuse.
# dagger-engine-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: dagger-engine
namespace: ci
spec:
selector:
matchLabels:
app: dagger-engine
template:
metadata:
labels:
app: dagger-engine
spec:
containers:
- name: dagger-engine
image: registry.dagger.io/engine:v0.20.3
securityContext:
privileged: true
ports:
- containerPort: 8080
name: api
volumeMounts:
- name: dagger-cache
mountPath: /var/lib/dagger
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
volumes:
- name: dagger-cache
hostPath:
path: /var/lib/dagger
type: DirectoryOrCreate
Actions Runner Controller (ARC) Integration
For GitHub Actions with Kubernetes runners, combine ARC with Dagger. ARC usage grew 45% year-over-year in 2026, making it the standard for ephemeral, auto-scaling runners.
Comparison with Existing CI/CD Tools
| Aspect | Dagger | GitHub Actions | Jenkins | GitLab CI |
|---|---|---|---|---|
| Pipeline Definition | Code (Go/TS/Py) | YAML | Groovy DSL | YAML |
| Local Execution | Full support | act (limited) | No | No |
| Vendor Lock-in | None | GitHub | None | GitLab |
| Caching | Auto incremental | Manual config | Plugins | Manual config |
| Debugging | OTel traces | Logs only | Blue Ocean | Logs only |
| Module Ecosystem | 1,500+ | 20K+ Marketplace | 1,800+ plugins | Limited built-in |
| K8s Native | DaemonSet | ARC | Agent Pod | Runner |
| AI Integration | Dang + Daggie | Copilot | Limited | Duo |
Important: Dagger is not a replacement for GitHub Actions — it's complementary. Keep your CI runner infrastructure (GitHub Actions, GitLab CI, etc.) and replace the YAML logic with Dagger functions. "Dagger replaces YAML, not your CI."
OpenTelemetry Integration — Pipeline Observability
Every Dagger operation automatically generates OpenTelemetry traces with granular logs and metrics, viewable in terminal or web interfaces.
# Configure OpenTelemetry export
export OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318
# Run pipeline — traces sent automatically
dagger call build --source=.
# Visualize every pipeline step in Jaeger UI
# See execution time, cache hits/misses, dependency graphs
Combined with Prometheus + Grafana, you can monitor pipeline execution time trends, cache hit rates, and failure rates on dashboards — deep trace-based analysis that was impossible with log-only YAML CI.
Performance Benchmarks — Real-World Data
OpenMeter Case Study (Published Data)
| Configuration | Build Time | Cost | Improvement |
|---|---|---|---|
| GitHub Actions alone | 25 min | Baseline | - |
| Dagger + Cloud caching | 10 min | -30% | 2.5x |
| Dagger + Cloud + Fast Runner | 5 min | -50% | 5x |
The key factor is incremental caching. The Dagger Engine caches each DAG node independently, re-executing only changed portions — fundamentally different from traditional CI that runs entire workflows from scratch.
GitHub Actions Runner Price Reduction
In January 2026, GitHub reduced runner pricing by up to 39%. The new 4-vCPU "Standard" runner costs the same as the 2024 2-vCPU runner. Combining these runners with Dagger enables 5x performance at half the cost.
Production Adoption Checklist
| Phase | Check Item | Recommendation |
|---|---|---|
| 1. Pilot | Apply Dagger to a single project | Run parallel with existing YAML, compare results |
| 2. SDK Selection | Use your team's primary language SDK | Recommend GA SDKs: Go/TS/Python |
| 3. Module Management | Pin external module versions, monorepo for internal | Supply chain security: Cosign signature verification |
| 4. Caching | Dagger Cloud caching or self-hosted cache | Per-node hostPath or PVC |
| 5. Observability | Configure OTel trace collection | Jaeger/Tempo + Grafana dashboard |
| 6. Security | Minimize privileged containers | Evaluate Sysbox or rootless mode |
| 7. Rollout | Team-wide adoption after pilot success | Build internal module library |
Conclusion — The Next Step for CI/CD
Dagger's proposition is clear: write CI/CD pipelines in code (not YAML), run them identically locally and in CI, and maximize speed with container-based incremental caching. At cdCon 2026, AI-powered pipeline optimization, platform engineering, and software supply chain security are key themes — and Dagger shows strength in all three areas.
The emergence of the Dang scripting language signals a future where AI agents create and modify pipelines. Its design — introspecting the Engine schema without codegen — is an interface optimized for LLMs to understand and manipulate pipelines. In the evolution from "humans writing YAML" to "AI agents orchestrating pipelines as code," Dagger is the most compelling runtime candidate.
You don't need to abandon GitHub Actions or GitLab CI today. Keep the YAML wrapper and gradually migrate pipeline logic to Dagger functions — that's the most practical adoption strategy. Start with a pilot on one project, and share the build time reduction and local debugging experience with your team. Once "works on my machine" problems disappear, going back to YAML becomes very difficult.
This article was written with AI assistance (Claude Opus 4.6). Technical accuracy was cross-verified against official documentation, release notes, and published case study data. For the latest version changes, check the Dagger Official Changelog.
© 2026 ManoIT · www.manoit.co.kr
Originally published at ManoIT Tech Blog.
Top comments (0)