Dagger Review — Is 'CI/CD as Code' Worth Ditching YAML For?
Dagger lets you write CI/CD pipelines in real programming languages instead of YAML, and run them identically on your laptop and in CI. I tried it on a real pipeline — here's the honest verdict.
Every CI/CD platform has the same debugging ritual: push a commit, wait for the pipeline to run, find a YAML mistake, push again, wait again. Dagger's pitch is direct — write your pipeline as code in Go, Python, or TypeScript, run it locally exactly as it runs in CI, and stop debugging YAML through a remote feedback loop.
I rebuilt a real pipeline (lint, test, build, push to registry) using Dagger to see if the pitch holds up.
The Core Idea
Dagger pipelines run inside containers, defined and orchestrated through an SDK call, not a YAML spec. The same pipeline code runs locally and in CI — because it's not interpreted by GitHub Actions or GitLab's runner-specific YAML schema, it's just calling Dagger's engine, which works the same everywhere.
# pipeline.py — runs identically on your laptop and in CI
import dagger
from dagger import dag, function, object_type
@object_type
class MyPipeline:
@function
async def test(self, source: dagger.Directory) -> str:
return await (
dag.container()
.from_("python:3.13-slim")
.with_directory("/app", source)
.with_workdir("/app")
.with_exec(["pip", "install", "-r", "requirements.txt"])
.with_exec(["pytest", "-v"])
.stdout()
)
@function
async def build_and_push(self, source: dagger.Directory, tag: str) -> str:
image = (
dag.container()
.from_("python:3.13-slim")
.with_directory("/app", source)
.with_workdir("/app")
.with_entrypoint(["python", "main.py"])
)
return await image.publish(f"myregistry.io/myapp:{tag}")# Run it locally — exact same execution path as CI
dagger call test --source=.
# It runs in CI by calling the exact same function
dagger call build-and-push --source=. --tag=v1.2.3What I Liked
Local reproduction actually works. This is the headline feature and it delivers. I deliberately broke a test, ran dagger call test locally, saw the same failure I'd see in CI — no "works on my machine, fails in CI" gap, because there's no separate "my machine" execution path. Everything runs in the same containerized environment Dagger controls.
Caching is genuinely smart. Dagger caches each layer of the pipeline graph, so re-running after a small source change only re-executes the steps actually affected by that change — similar to Docker layer caching but applied to your whole pipeline logic, not just image builds.
You get real programming language tooling. Type checking, IDE autocomplete, unit-testable pipeline logic, proper error handling with try/except instead of YAML's limited conditional expressions. Writing a pipeline that conditionally skips steps based on changed files is a if statement, not a YAML anti-pattern.
Portable across CI providers. The same pipeline code can run on GitHub Actions, GitLab CI, or Jenkins with a thin wrapper, because the actual logic lives in Dagger functions, not provider-specific YAML. Migrating CI providers becomes much less painful.
What I Didn't Like
The learning curve is real, and it's not small. If your team writes YAML pipelines today, Dagger isn't a drop-in replacement — it's a different mental model (container graph composition via SDK calls) that takes genuine ramp-up time, especially for teams without strong programming backgrounds writing the pipelines.
Debugging the Dagger engine itself, when something goes wrong, is harder than debugging YAML. A YAML syntax error gives you a clear line number. A Dagger pipeline failure inside the engine's container orchestration layer gives you a less immediately obvious error sometimes — you're debugging through an abstraction layer, not directly at the YAML level.
Smaller ecosystem of pre-built actions. GitHub Actions Marketplace has thousands of community actions for common tasks (Slack notifications, security scanning, deployment to specific platforms). Dagger's module ecosystem is growing but nowhere near that breadth yet — you'll write more glue code yourself for integrations that would be a one-line uses: in GitHub Actions.
Still need a CI runner underneath. Dagger doesn't replace GitHub Actions or GitLab CI entirely — you still need something to trigger the pipeline on push/PR. Dagger replaces the pipeline logic, not the trigger/orchestration layer around it. Your .github/workflows/ci.yml shrinks to almost nothing, but it doesn't disappear.
Honest Positioning
| Dagger | Plain GitHub Actions/GitLab CI | Tekton | |
|---|---|---|---|
| Local reproduction | Excellent | Poor (act/gitlab-runner approximate it) | Poor |
| Learning curve | Medium-high | Low | High |
| Ecosystem/actions | Growing, smaller | Huge | Medium |
| Cross-CI portability | High | Low | Medium |
| Best for | Complex pipelines, polyglot teams | Simple-to-medium pipelines | Kubernetes-native CD |
My Assessment
Dagger earns its keep on pipelines complex enough that YAML's limitations — no real loops, awkward conditionals, painful local testing — are actively costing you time. If your current pipeline is 200+ lines of YAML with reusable workflow includes and conditional logic that's hard to follow, Dagger's programming-language approach is a real upgrade.
If your pipeline is "install deps, run tests, build, push" in under 50 lines of YAML, the migration cost isn't worth it — you'd be trading a working simple thing for a more powerful but more complex thing you don't need yet.
Worth piloting on one complex pipeline before committing your whole CI to it. The local-reproduction benefit alone is worth testing on whichever pipeline currently wastes the most developer time on "push and wait to see if it worked."
Compare it against the established CI tools: GitHub Actions vs GitLab CI vs CircleCI
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
AI Coding Assistants Will Change DevOps — But Not in the Way You Think
GitHub Copilot, Cursor, and Claude are already writing infrastructure code. But the real disruption isn't replacing DevOps engineers — it's reshaping what the job actually is.
Build an Internal Developer Platform with Backstage (2026)
Step-by-step guide to setting up a Backstage developer portal — software catalog, TechDocs, Kubernetes plugin, and golden path templates.
Continuous Verification: The Post-Deploy Testing Methodology That Replaces 'Hope-Driven Ops'
CI/CD tests tell you your code works in a test environment. Continuous Verification tells you your code works in production, on real traffic, right now. Here's the methodology, the tools, and why it's becoming the standard for mature engineering teams.