Skip to main content

Lockfiles

Dagger automatically records the exact results of supported symbolic lookups in dagger.lock. Later runs reuse those recorded results, making container image and Git lookups reproducible.

Locking is enabled by default. Missing entries are resolved normally and written to the lockfile; existing entries are reused.

Lockfile format

New lockfiles use format version 2. Each non-empty file starts with a version header followed by one JSON tuple per lookup:

[["version","2"]]
["","container.from",["docker.io/library/alpine:latest","linux/amd64"],"sha256:..."]

Each entry contains, in order:

  1. namespace
  2. operation
  3. lookup inputs
  4. resolved value

Entries are ordered deterministically by namespace, operation, and inputs.

Version compatibility

Dagger reads both version 1 and version 2 lockfiles. Current API versions write version 2.

Version 1 entries include a fifth policy value, pin or float. Current API versions preserve that behavior while reading them:

  • A legacy pin entry is reused.
  • A legacy float entry is resolved again when encountered.

Any subsequent write serializes the whole lockfile as version 2 and removes the policy from every entry. Values that were not encountered remain unchanged and become pinned; encountered float entries use their newly resolved values.

Version 2 does not store policies. Every recorded result is pinned until explicitly updated.

Clients using an API view from before pinned-by-default locking ignore the lockfile entirely. They neither read nor write it, even when run by a newer engine. This prevents an older module from silently acquiring new locking semantics.

Unknown future lockfile versions are rejected instead of being interpreted with potentially incompatible semantics.

Updating lockfiles

Use dagger update to refresh entries already recorded in dagger.lock:

dagger update

The update uses the current environment ambient authentication. Run it somewhere that can authenticate to any private registries or repositories referenced by the lockfile.

Ordinary commands discover and record missing entries automatically. There is no separate lock mode to select.

Lock-aware operations

These operations currently record and resolve lock entries:

OperationInputsResult
container.from[imageRef, platform]image digest
git.ref[remoteURL, selector]{"sha":"<commit>","ref":"<canonical-ref>"}

All symbolic Git lookups use git.ref. The selector records the original lookup intent: HEAD for git.head, a fully qualified refs/heads/... or refs/tags/... selector for git.branch and git.tag, and the caller's selector for git.ref.

The result always records the resolved commit SHA. It also records the canonical ref when Git reports one. A migrated version 1 git.head pin can omit ref because the old entry did not record the remote's default branch; reading it does not contact the remote or guess a branch name.

git.commit is already pinned by its input and does not create a lock entry.