need for speed

Project Theseus: removing BuildKit

The largest engine change since the project began. Dagger is replacing BuildKit's solver with its own native engine, unlocking robust remote caching, cache observability, and more flexible module execution.

Every container operation delegated to BuildKit is being reimplemented natively in Dagger's DAG engine. Internally called 'Project Theseus' — after the Ship of Theseus — the team has spent over a year replacing BuildKit's internals one plank at a time, shipping continuously at every step. The key was a transitional facade layer called DagOp: each operation (git, HTTP, container exec, file and directory operations, exports) was individually lifted out of BuildKit and into Dagger's own engine. Over 50 PRs later, the facade has served its purpose and is being removed. In its place: a caching engine built on e-graphs — a data structure from compiler optimization research that tracks equivalences between operations, maximizing cache hit rates beyond what any conventional build cache can achieve. The new cache has a single, understandable code path for key calculation and lookup. A long-standing caching bug was fixed without anyone investigating it — the scenario that confused BuildKit's two intertwined caching systems is simply handled correctly by a system simple enough to reason about.

Modules v2

A major upgrade to how modules interact with your project. Modules get a typed API to your workspace, replacing rigid sandbox constraints with dynamic context — less complexity for developers, more control for platform engineers.

Today, modules can't see your project structure unless you thread it through manually with custom boilerplate code, wrapper shell scripts, and static path patterns. Modules v2 changes this with a typed Workspace argument that replaces +defaultPath and +ignore pragmas with real code — capable of parsing config files, efficiently traversing directory trees, and adapting to any project layout and scale. A new .dagger/config.toml file declares which modules your project uses and how to configure them, in human-editable TOML. The module author handles the complexity; you just install and configure. A lockfile pins module versions for reproducible resolution across teams.

cloud

Cloud Engines

Fully managed Dagger engines with auto-scaling and distributed caching. Run dagger --cloud and your pipelines execute in the cloud — with your local context preserved seamlessly. In early access.

No Kubernetes daemonsets, no handrolled load balancing, no cache distribution headaches. Compute and cache run in the cloud, but local secrets, service dependencies, configuration, files, and terminal are securely streamed — all available to your pipeline as if it were running on your machine. Run your full integration test suite with the compute and cache of your production CI, against your uncommitted changes, with secrets from your personal vault. We use Cloud Engines in production for our own CI and are onboarding our first customers.

cloud

Cloud Checks

A complete replacement for your CI platform. Cloud Checks connects to your Git provider and triggers dagger check on every change, auto-scaled on Cloud Engines — no YAML, no vendor syntax, no orchestration layer. In early access.

If your CI logic is in Dagger modules, and your CI infrastructure is on Cloud Engines — you don't need a third-party CI platform to glue them together. Cloud Checks processes Git events and runs your checks on managed infrastructure that scales with your team. Just your modules, your checks, and nothing else. We've already unplugged GitHub Actions for our own CI — join our Discord if you want in.

dagger up

The third standard workflow verb, joining dagger check and dagger generate. One command starts all services in your workspace — discovered automatically, started in parallel, ports tunneled to your host.

Annotate any function that returns a Service with +up, and dagger up discovers it across your workspace — including services from your dependencies. Port collisions are caught before anything starts, and container health checks gate readiness automatically. No YAML, no separate orchestration layer — just functions in your modules, composed through the same engine that runs your builds and tests.

dagger up

dagger up --list

dagger up web redis

Fail-fast mode for dagger check

A new --failfast flag cancels remaining checks as soon as one fails. Designed to give AI agents a faster feedback loop — instead of waiting for an entire suite to finish, agents get the first failure immediately and can start fixing it.

dagger check --failfast

Go SDK improvements

Go codegen now splits dependency types into separate files — one per dependency instead of one monolithic dagger.gen.go. Makes it easy to see which generated code belongs to which dependency, and sets up automatic cleanup when dependencies are removed.

need for speed

Dang: a Dagger-native language

A scripting language designed specifically for Dagger, now with a native runtime built into the engine. Dang loads types directly from the engine at runtime — no codegen, near-instant startup, and concise syntax that's a natural fit for AI-assisted development.

Traditional Dagger SDKs generate client code from the API schema before your module can run. Dang skips this entirely: it introspects the schema at runtime and makes every Dagger type a native language type. With the new native runtime, Dang modules run directly inside the engine without the usual module loading overhead — no container startup, no process coordination. An entire module with multiple functions and types fits in under a hundred lines.

New terminal UI

A ground-up rebuild of the interactive TUI. Output uses your terminal's native scrollback — scroll freely, click links, select text — no more fixed viewport. Also adds inline search with /.

The old framework (Bubbletea) has been replaced with a new rendering engine built on Tuist, a TUI framework designed for infinite scrollback. Each span in the trace tree is an independent component with its own render cache — only components whose data changed get repainted, using line-level diffs to minimize terminal I/O. The result: a TUI that feels like normal terminal output, but with the rich structure of a trace viewer. Mouse input is no longer captured, so scrolling always works.

Vault OIDC authentication

The Vault secret provider now supports OIDC login. If no token or environment credentials are set, Dagger opens your browser for OIDC authentication and caches the token with automatic expiry — no manual token management needed.

ruggedize

Stability fixes

Running Dagger inside a container with a mounted git worktree no longer crashes. Module dependencies pinned by branch or tag name instead of commit SHA now resolve correctly — a regression from the faster Git fetch optimization. The CLI no longer exits 0 when a command actually failed. Containers now run with --init to reap zombie processes. Fixed a schema loading cache regression that could cause incorrect module resolution. Cached git auth secrets that go missing no longer cause fatal errors. The TUI no longer steals focus from form inputs.

need for speed

Faster Git remote fetches

Remote Git fetches now skip tag transfer by default, cutting cold fetch time by ~80% on tag-heavy repos. A bounded retry handles servers that reject direct SHA fetches.

Service API honors Docker healthchecks

Services can now use Docker HEALTHCHECK commands to determine readiness, replacing the default port-based check. Define healthchecks in a Dockerfile or with the new withHealthcheck() API.

srv := dag.Container().
  From("python").
  WithDefaultArgs([]string{"python", "-m", "http.server", "8080"}).
  WithHealthcheck([]string{"curl", "-sf", "http://localhost:8080/"},
    dagger.ContainerWithHealthcheckOpts{
      Timeout:   "3s",
      Retries:   3,
    }).
  AsService()

Improved Changeset API

The Changeset API now supports creating empty changesets and merging arrays of them in one call. When a function returns multiple changesets, the CLI merges them and offers one combined apply prompt.

need for speed

Faster Java SDK builds

The Java SDK runtime now invokes Maven with --threads 1C by default, using one thread per CPU for better build throughput on multi-core machines.

Targeted local-cache pruning

Manual local-cache pruning now supports explicit space thresholds, so teams running with automatic GC disabled can prune predictably without changing global policy.

Set per-call overrides for max used space, reserved space, minimum free space, and target space.

cloud

More reliable GitHub Checks integration

Dagger Cloud now integrates more reliably with GitHub Checks. Checks move to queued as soon as events are received, continue running even when GitHub API calls are delayed, and avoid noisy failures in repositories without a dagger.json module config. We also expanded commit-status reporting beyond Native CI workflows, so per-check results appear more consistently with direct links back to traces.

cloud

Checks keep running during GitHub outages

Dagger Cloud checks no longer block on GitHub API availability. If GitHub is delayed or temporarily unavailable, checks continue running and status updates are handled asynchronously, reducing flaky failures during incidents.

Dagger Generate

We added dagger generate for code and config generation workflows. Mark generator functions in your module, return Changesets, and Dagger runs and merges them in one pass.

Great for generated clients, repo scaffolding, and config drift cleanup without stitching together shell scripts.

dagger generate

Dagger Cloud v3

Dagger Cloud v3 delivers a new WebAssembly-based UI with better trace navigation, clearer error surfacing, and much faster handling of large traces and log volumes.

This is a Dagger Cloud release (not an open source engine release). To opt in, sign in to dagger.cloud and switch the top-right toggle to v3.

File metadata API

directory.stat() and container.stat() expose metadata directly through the API, removing brittle shell parsing for common file-inspection tasks.

JSON API

Lightweight JSON edits are now practical inside Dagger pipelines. Instead of shelling out to jq, read, modify, and write JSON through typed APIs.

@func()
async setPackageManager(src: Directory): Promise<Directory> {
  const updated = src
    .file("package.json")
    .asJSON()
    .withField(["packageManager"], dag.json().newString("pnpm@10.5.2"))

  return src.withFile(
    "package.json",
    dag.file("package.json", await updated.contents({ pretty: true })),
  )
}
need for speed

Faster file copies

WithDirectory and WithFile were optimized to avoid unnecessary disk copies in more cases, cutting avoidable I/O overhead in common workflows.

Dagger Checks

dagger check gives module developers one command to run validation anywhere: local dev, CI, and agents. Define checks once, run them consistently everywhere.

Checks are supported in Go, Python, and TypeScript, with Java support added in v0.19.11.

dagger check

dagger check -l

dagger check 'lint-*'
need for speed

Function Cache Control

Before this change, Dagger cached all system functions but not module developers' own functions. We couldn't safely infer purity from side effects, so default caching could have broken behavior.

Now module developers can explicitly set cache policy per function. With explicit control in place, default caching can improve performance safely.

@func() // pure function: default TTL caching
async test(): Promise<string> { ... }

@func({ cache: "15m" }) // refresh external data periodically
async latestBaseDigest(): Promise<string> { ... }

@func({ cache: "never" }) // side effects: always execute
async deploy(): Promise<string> { ... }

Changeset API

The new Changeset type lets module developers return a directory diff as an artifact, so generated files can be previewed and applied cleanly from the CLI.

Container.combinedOutput

Need stdout and stderr exactly as emitted? Container.combinedOutput returns a single merged stream, so module developers can handle real command output without stitching streams back together.

dagger shell -c 'container | from alpine | with-exec ls,-l,/etc/fstab,/etc/non-existent --expect=any | combined-output'

Address API

The new address API exposes Dagger's CLI-style reference parsing to modules, so module developers can turn user input into typed objects without reimplementing parsing logic.

Cloud.traceURL

Modules can now fetch the current Dagger Cloud trace URL directly. This makes it easy to publish clickable run links in logs, summaries, and CI notifications.

traceURL, err := dag.Cloud().TraceURL(ctx)
if err != nil {
  return "", err
}
return traceURL, nil

Git reference introspection APIs

Git-heavy modules got better primitives for reasoning about repository state: gitRepository.url, gitRepository.branches, gitRepository.latestVersion, and gitRef.commonAncestor.

Together, these APIs make it much easier to resolve origins, enumerate refs, pick stable versions, and compute merge bases without shelling out.

Dagger Shell

Dagger Shell brought bash-style syntax to Dagger's typed, container-native execution model. You get shell ergonomics with cached, reproducible pipelines and sandboxed-by-default execution.

It also made it easier to compose bigger workflows in one place: simple shell pipelines for day-to-day work, code when logic grows, all on the same system.

container |
  from alpine |
  with-exec apk add git