🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

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.

DevOpsBoysJun 16, 20264 min read
Share:Tweet

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.

python
# 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}")
bash
# 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.3

What 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

DaggerPlain GitHub Actions/GitLab CITekton
Local reproductionExcellentPoor (act/gitlab-runner approximate it)Poor
Learning curveMedium-highLowHigh
Ecosystem/actionsGrowing, smallerHugeMedium
Cross-CI portabilityHighLowMedium
Best forComplex pipelines, polyglot teamsSimple-to-medium pipelinesKubernetes-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

Browse fixes
Newsletter

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

Comments