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

Earthly Review 2026: CI/CD Build Tool That Actually Works Everywhere

Honest review of Earthly after using it for Docker-based builds across GitHub Actions, GitLab CI, and local development. What it solves and where it falls short.

DevOpsBoys4 min read
Share:Tweet

Earthly promises to solve the "it works on my machine" problem for CI/CD builds by making your build process a first-class, reproducible artifact. After using it on two production projects over 6 months, here's what I actually think.

What Earthly Is

Earthly is a build tool that combines Dockerfile-like syntax with Makefile-like targets. You write an Earthfile, and Earthly runs it in a container — giving you reproducibility across local dev and CI environments.

earthfile
# 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 bin/server ./cmd/server
    SAVE ARTIFACT bin/server /server AS LOCAL ./bin/server
 
docker:
    FROM alpine:3.19
    COPY +build/server /server
    ENTRYPOINT ["/server"]
    SAVE IMAGE myapp:latest
 
test:
    FROM +build
    RUN go test ./...
 
lint:
    FROM golangci/golangci-lint:v1.57
    WORKDIR /app
    COPY . .
    RUN golangci-lint run
 
all:
    BUILD +test
    BUILD +lint
    BUILD +docker

Run locally:

bash
earthly +all

Same command in GitHub Actions, GitLab CI, or Jenkins. The build runs inside a container on every platform.

What's Actually Good

Local = CI Parity

This is the main value prop and it genuinely works. When a developer runs earthly +test locally, they get the exact same environment as CI. No more "CI uses Ubuntu 22 but my Mac runs on something different" debugging.

yaml
# .github/workflows/ci.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Earthly Build
        uses: earthly/actions-setup@v1
      - run: earthly +all
        env:
          EARTHLY_TOKEN: ${{ secrets.EARTHLY_TOKEN }}

That's it. Same earthly +all command as local.

Caching Works Well

Earthly caches at the layer level (like Docker) but across targets. If go mod download didn't change, it's cached — even when you change application code.

earthfile
deps:
    FROM golang:1.22-alpine
    WORKDIR /app
    COPY go.mod go.sum .
    RUN go mod download  # cached as long as go.mod doesn't change
    SAVE IMAGE
 
build:
    FROM +deps  # reuses cached deps layer
    COPY . .
    RUN go build -o bin/server ./cmd/server

With remote caching (Earthly Cloud or self-hosted), this cache is shared across your whole team and CI runners:

bash
earthly --remote-cache=registry.example.com/myapp/cache +build

Multi-Platform Builds

Earthly makes cross-platform builds (arm64/amd64) straightforward:

earthfile
docker-multiplatform:
    BUILD --platform=linux/amd64 --platform=linux/arm64 +docker

Monorepo Support

For monorepos, Earthly shines. You can reference targets across projects:

earthfile
# services/api/Earthfile
VERSION 0.8
FROM DOCKERFILE .
 
build:
    COPY +../../shared/proto/proto /proto
    RUN protoc --go_out=. /proto/*.proto
    RUN go build ./...

What's Not Great

Learning Curve for Complex Builds

The Earthfile syntax is its own language. Teams used to pure Dockerfiles or shell scripts take time to adapt. Complex conditional logic is awkward.

Earthly Cloud Cost

The remote caching and satellite (faster builds) features require Earthly Cloud, which is paid. The free tier is generous for small teams, but it's an additional SaaS dependency.

You can self-host the cache with:

bash
earthly --remote-cache=your-registry/cache:earth +target

But it requires more setup than using Earthly Cloud.

Docker Daemon Required

Earthly requires Docker to be running. In some restricted CI environments (serverless, rootless), this is a problem. BuildKit-based alternatives like Dagger don't have this requirement.

Debugging Is Harder Than Docker

When a build fails inside Earthly, the error context can be less clear than a plain Dockerfile. You lose the interactive shell experience you'd get with docker build.

Earthly vs Alternatives

EarthlyDaggerPure Docker/MakefileGitHub Actions
ReproducibilityExcellentExcellentGoodPoor (runner env varies)
Learning curveMediumMedium-HighLowLow
Local dev supportExcellentExcellentGoodPoor
LanguageEarthfileGo/Python/TS SDKShell/MakefileYAML
CachingExcellent (layer-based)ExcellentManualWith setup
Docker requiredYesNoYesDepends

Real-World Performance

On a Go microservice with ~15k lines of code:

Build stepBefore EarthlyAfter Earthly (warm cache)
go mod download45s per CI run2s (cached)
go build60s15s (partial cache)
golangci-lint90s8s (cached)
Total~3m 30s~35s

Those numbers are realistic when the remote cache is warm. First run after a dependency change is slower.

My Verdict

Score: 7/10

Earthly genuinely solves the local/CI parity problem and the caching is excellent. If your team has complained about "works locally but fails in CI" more than twice, Earthly is worth evaluating.

The tradeoff is another tool to learn and maintain. For simple pipelines or teams that prefer YAML, GitHub Actions caching with Docker BuildKit achieves 80% of what Earthly does with less adoption friction.

Use Earthly if:

  • You have complex multi-stage builds
  • You want local dev to match CI exactly
  • You're in a monorepo with multiple services

Skip Earthly if:

  • Your builds are simple (Dockerfile + 3 steps)
  • Your team has low appetite for new tooling
  • You're in a rootless or restricted CI environment

Earthly docs | Dagger (alternative worth evaluating)

🔧

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