Pinning base image digests with SHA256

A tag like node:20 is a moving pointer — the registry can repoint it to a new build at any time, so two docker pulls a month apart can yield different images and break reproducibility silently. Pinning to the immutable @sha256:... digest makes the base image byte-identical forever. This page shows how to find, pin, and maintain digests. It extends the Container Registry Best Practices for Dev Images cluster.

Prerequisites

  • Docker Engine 24+ or a compatible runtime
  • Pull access to the registry hosting the base image

How-To Steps

  1. Resolve the digest for the tag you currently use:

    docker pull node:20-bookworm
    docker inspect --format '{{ index .RepoDigests 0 }}' node:20-bookworm
    # e.g. node@sha256:3f9a...c1
    
  2. Pin the digest in devcontainer.json, keeping the human-readable tag as a comment in your docs:

    {
      "image": "node:20-bookworm@sha256:3f9a...c1",
      "remoteUser": "node"
    }
    
  3. Pin in the Dockerfile FROM when you build your own image:

    FROM node:20-bookworm@sha256:3f9a...c1
    
  4. Pin Features too — they are OCI artifacts with digests:

    { "features": { "ghcr.io/devcontainers/features/node@sha256:7b2e...": { "version": "20" } } }
    
  5. Verify the running image matches the pin:

    devcontainer exec --workspace-folder . sh -c 'cat /etc/os-release | head -1'
    docker inspect --format '{{.Image}}' "$(docker ps -ql)"   # matches the pinned digest
    

Common Pitfalls

SymptomRoot CauseRemediation
Build differs across machines/timeFloating tag repointed by the registryPin @sha256: digest
Digest pull failsDigest garbage-collected from the registryMirror critical images to your own registry
Security scan flags old baseDigest pinned and never refreshedSchedule a controlled digest-bump PR
Multi-arch pull picks wrong archPinned a single-arch digestPin the manifest-list digest instead

Conclusion

The invariant: every base reference — image, Dockerfile FROM, and Features — is pinned to a SHA256 digest, so the environment is byte-reproducible. Pinning is not “set and forget”: pair it with a scheduled, reviewed digest-bump so you stay reproducible and patched.

FAQ

Doesn’t pinning prevent security updates? It prevents silent ones. You still update — through a deliberate PR that bumps the digest, so the change is reviewed and reproducible rather than arriving unannounced.

Can I pin a multi-arch image by digest? Yes — pin the manifest-list digest. The runtime still selects the correct per-architecture image beneath it, as covered in multi-architecture builds.