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

Dagger vs Earthly vs GitHub Actions: Which CI Tool Actually Solves Your Problems?

GitHub Actions is everywhere but Dagger and Earthly are challenging it hard. Here's an honest comparison of all three — performance, portability, learning curve, and when to use each.

DevOpsBoysJun 12, 20266 min read
Share:Tweet

The CI/CD tooling space just got interesting again.

For years, the conversation was "Jenkins vs GitHub Actions vs GitLab CI." Boring. Solved. Everyone migrated to GitHub Actions and moved on.

Then Dagger and Earthly showed up and asked an uncomfortable question: why does your pipeline only run in the cloud? Why can't you run it locally, get the same result, and cache it properly?

Here's what I've learned from using all three in production.

The Core Problem Each Tool Is Solving

Before comparing them, understand what problem each one is actually designed for:

GitHub Actions solves: "I need CI/CD that works with GitHub and requires minimal setup."

Earthly solves: "My Dockerfile builds are slow and I can't reproduce CI failures locally."

Dagger solves: "I want my entire pipeline to be code — portable, testable, and runnable anywhere."

These are different problems. The "best" tool depends on which problem you're actually having.


GitHub Actions

GitHub Actions is the default for a reason. It's deeply integrated with GitHub, it has 20,000+ community actions in the Marketplace, and for most teams it just works.

What it does well:

  • Zero infrastructure to manage
  • Native integration with GitHub events (PRs, pushes, releases, issue comments)
  • Excellent secret management
  • Matrix builds for cross-platform testing
  • Free for public repos, generous free tier for private

Where it breaks down:

  • YAML-heavy — complex pipelines become YAML files that are 500 lines long and unmaintainable
  • Debugging is painful — you push a commit, wait 3 minutes for the runner, see the error, push another commit. Repeat 8 times.
  • No local execution — act exists but it's a partial approximation, not true parity
  • Caching is manual and fragile — getting proper layer caching right in GitHub Actions is genuinely hard
  • Cost scales badly for large teams with frequent commits
yaml
# A typical GitHub Actions job
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: myapp:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

This works. It's just YAML. There's no abstraction, no testability, no reusability without copy-pasting.


Earthly

Earthly describes itself as "Make + Dockerfile, but for CI." That's accurate.

You write an Earthfile that looks like a cross between a Makefile and a Dockerfile. Earthly runs builds inside containers, which means your builds are reproducible by definition — the same container, the same filesystem, the same result locally and in CI.

earthfile
VERSION 0.8
 
build:
    FROM golang:1.22-alpine
    WORKDIR /app
    COPY go.mod go.sum .
    RUN go mod download
    COPY . .
    RUN go build -o myapp .
    SAVE ARTIFACT myapp
 
docker:
    FROM alpine:3.19
    COPY +build/myapp /usr/local/bin/myapp
    ENTRYPOINT ["/usr/local/bin/myapp"]
    SAVE IMAGE myapp:latest

Run locally: earthly +docker Run in CI: same command.

What Earthly does well:

  • True build reproducibility — if it works locally it works in CI
  • Aggressive caching — Earthly caches at the layer level and shares cache between targets
  • Parallel builds — targets can declare dependencies and run in parallel automatically
  • Language agnostic — works for Go, Node, Python, anything that runs in a container
  • Drop-in for existing CI — runs inside GitHub Actions, GitLab CI, Jenkins

Where it breaks down:

  • Another DSL to learn (Earthfile syntax)
  • Remote caching requires Earthly Satellites (paid) or self-hosted cache server
  • Not as flexible as writing raw pipeline logic in a real programming language
  • Community is smaller — fewer examples, less StackOverflow help

Earthly is the right choice when your main pain is "builds work locally but fail in CI" or "our Docker builds are slow and caching is broken."


Dagger

Dagger is the most ambitious of the three. Solomon Hykes (Docker co-creator) built it to answer a fundamental question: why do we write CI pipelines in YAML when we have perfectly good programming languages?

In Dagger, your pipeline is code. Real code. You write it in Go, Python, TypeScript, or PHP. You import Dagger modules. You test your pipeline like any other code.

python
import dagger
from dagger import dag, function, object_type
 
@object_type
class MyPipeline:
    @function
    async def build_and_push(self, src: dagger.Directory) -> str:
        return await (
            dag.container()
            .from_("golang:1.22-alpine")
            .with_directory("/app", src)
            .with_workdir("/app")
            .with_exec(["go", "build", "-o", "myapp", "."])
            .publish("ttl.sh/myapp:latest")
        )

This is a real Python function. You can write unit tests for it. You can import it into other pipelines. You can run it locally with dagger call build-and-push --src .

What Dagger does well:

  • Pipelines in real programming languages — testable, refactorable, reviewable
  • True portability — same code runs locally, in any CI system
  • Strong caching — Dagger caches at the operation level, not just layers
  • Module ecosystem — publish and consume pipeline components like libraries
  • Type safety — your pipeline arguments are typed, not stringly-typed YAML

Where it breaks down:

  • Steeper learning curve — you need to understand Dagger's execution model
  • Slower adoption — fewer engineers know it, hiring/onboarding is harder
  • Overkill for simple pipelines — a 50-line GitHub Actions file doesn't need to become a Python module
  • Still maturing — some rough edges in the SDK, documentation gaps

Head-to-Head Comparison

CriteriaGitHub ActionsEarthlyDagger
Local executionPartial (act)YesYes
Build reproducibilityNoYesYes
LanguageYAMLEarthfile DSLGo/Python/TS/PHP
CachingManual, fragileAutomatic, goodAutomatic, excellent
GitHub integrationNativeVia actionVia action
Learning curveLowMediumHigh
Community sizeHugeMediumGrowing
Self-hosted optionYesYesYes
CostGitHub pricingFree + paid cacheOpen source

Which One Should You Choose?

Choose GitHub Actions if:

  • Your team is small and pipelines are simple
  • You don't have the bandwidth to learn a new tool
  • Deep GitHub integration matters (PR status checks, environments, deployments)
  • You're running less than 200 CI minutes/day

Choose Earthly if:

  • "Works on my machine but fails in CI" is a real problem you face weekly
  • Your Docker builds are slow and caching is broken
  • You want reproducibility without rewriting your entire pipeline
  • You want to keep using GitHub Actions/GitLab CI as the outer shell

Choose Dagger if:

  • Your CI pipeline is complex enough to benefit from real programming abstractions
  • You have engineers who are comfortable writing Go or Python
  • You want to test your pipeline code like application code
  • You're building a platform that other teams consume

The Honest Take

GitHub Actions wins on ecosystem and ease of setup. That's real and it matters.

But the "write YAML for everything" model has real costs at scale. When your CI breaks at 3 AM and you can't reproduce it locally, when your caching isn't working and every build takes 8 minutes, when your pipeline YAML has grown to 600 lines — that's when Earthly or Dagger start looking less like "new shiny things" and more like "solutions to real problems I have."

Most teams don't need to abandon GitHub Actions. But adding Earthly for the build layer, while keeping GitHub Actions as the orchestration layer, is a pattern that works well in practice.

Generate your GitHub Actions pipeline instantly: CI/CD Pipeline Generator

🔧

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