Skip to content
OpsAtlas

Pin Docker Base Images by Digest and Keep Them Updated

By OpsAtlas · · 3 min read

Table of contents

FROM node:22-slim looks like a fixed version, but it is a pointer. The image it refers to changes every time Node.js ships a 22.x release or Debian publishes a security fix. Rebuild the same commit next month and you may get a different base image, sometimes with a behaviour change that breaks the build or the app.

That is fine until you need to reproduce an old build, roll back exactly, or explain why an image that worked yesterday doesn’t today. The answer is to pin the base image to its digest and let a bot move the pin forward.

Tags move, digests don’t

FROM lineWhat you getChanges when
node or node:latestThe newest releaseEvery release, including major versions
node:22-slimThe newest 22.x on Debian slimEvery 22.x release and base OS update
node:22.11.0-slimA specific Node.js versionThe image can still be rebuilt, for example for Debian security fixes
node:22-slim@sha256:…Exactly one imageNever

A tag is a name that the publisher can point at a new image at any time. Official images are rebuilt regularly under the same tags, so even an exact version tag is not a guarantee. A digest is the SHA-256 of the image manifest, so it always means the same bytes.

Find the digest

docker buildx imagetools inspect node:22-slim

The Digest: line at the top is the digest of the multi-platform index. Pin that one, not the digest of a single platform, so the same FROM line still works for both amd64 and arm64 builds.

FROM node:22-slim@sha256:<digest-from-the-command-above>

When a digest is present, Docker ignores the tag and pulls by digest. Keep the tag anyway: it tells people which version the digest is, and it tells update bots which tag to follow.

Let a bot move the pin

A pinned image never gets security fixes on its own. Pinning without automated updates trades surprise breakage for slowly rotting images. Both popular bots can update digests:

Renovate. Add the docker:pinDigests preset to renovate.json. Renovate pins existing FROM lines to digests in a first PR, then opens PRs whenever the tag points at a new image:

{
  "extends": ["config:recommended", "docker:pinDigests"]
}

If you group updates, put digest bumps in the same weekly PR as other non-major updates. You get one review a week instead of a PR every time Debian ships a fix.

Dependabot. Add the docker ecosystem to .github/dependabot.yml. Dependabot updates the tag and the digest of each FROM line it finds in the directory:

version: 2
updates:
  - package-ecosystem: docker
    directory: /
    schedule:
      interval: weekly

Each update is an ordinary pull request, so your CI builds the new base image and runs the tests before anything is merged. That is the review step a moving tag skips.

Pin every stage

In a multi-stage build, the build stages matter too. An unpinned golang:1.23 builder can change the compiler between two builds of the same commit. Pin every FROM, not just the last one.

Avoid putting the version in an ARG (FROM node:${NODE_VERSION}-slim) unless you also tell your update bot about it. Bots read literal FROM lines; a version hidden in a variable usually never gets updated.

The same rule applies in Kubernetes

A Deployment that references image: myapp:latest has the same problem one level up. Nodes that pull at different times can end up running different images under the same name. Release with a version tag, and pin to a digest when you need guarantees. Your CI can write the digest of the image it just pushed into the manifest.

Check your files

The Dockerfile linter flags every FROM that uses :latest or no tag at all, in every stage, and suggests the pinned form. A versioned tag such as node:22-slim passes, since pinning by digest only makes sense with an update bot. The Kubernetes YAML validator does the same for container images in your manifests.

Pinning is one of several habits that make images predictable. For the others, see Dockerfile mistakes that leak secrets and bloat images and multi-stage Docker builds.

Try it yourself

  • Dockerfile linter and fixer

    Lint a Dockerfile in your browser: find root users, unpinned images, leaked secrets and bloated layers, then apply safe fixes in one click.

    DockerBeta
  • Kubernetes YAML validator and visualizer

    Paste Kubernetes manifests to get a resource diagram and a security and best-practice review: privileged pods, root, probes, limits, deprecated APIs.

    KubernetesBeta