Base Image

A base image is the image a Dockerfile FROM instruction names, supplying the filesystem a build starts from. How the reference resolves and invalidates cache.

A base image is the image named in a Dockerfile FROM instruction, and it supplies the filesystem and image configuration that the build starts from. Every instruction after FROM adds a layer on top of that starting point, so the base image decides which shell, package manager, libraries, and default user exist before the first RUN line executes.

The reference in a FROM line is usually a mutable tag rather than a fixed identity. That single detail explains most base image surprises inside a GitHub Actions workflow: the tag can point at new content between two runs of the same commit, and the build behaves differently as a result.

Definition

The Dockerfile reference defines FROM as the instruction that initializes a new build stage and sets the base image for the instructions that follow (Dockerfile reference, FROM, checked on 2026-08-13). The image it names contributes two things to the build:

  1. A root filesystem, delivered as an ordered set of layers. Anything the build expects to find before it installs something (a libc, a shell, certificate bundles, a package manager, a language runtime) either arrives here or has to be added by a later instruction.
  2. An image configuration, which is a JSON document carrying ENV, WORKDIR, USER, ENTRYPOINT, CMD, exposed ports, and labels. These values are inherited as the defaults of the image being built and stay in effect until an instruction overrides them.

One reserved name has no filesystem at all. FROM scratch starts a stage from an empty layer set, which is how statically linked binaries and the published minimal images are assembled.

Terminology drifted over time and both usages are still in circulation. Older Docker documentation reserved "base image" for an image with no parent, built FROM scratch, and called the image your Dockerfile sits on top of the "parent image". Current usage treats the image named in FROM as the base image, which is the sense used on this page and the sense a build tool error message means.

One base image per stage

Each FROM instruction opens a new stage, so the count of base images in a Dockerfile equals the count of FROM lines. A build that compiles in a toolchain image and ships the result in a slim runtime image names two base images, and only the final stage contributes layers to the published result. The multi stage build entry covers how stages are named, selected, and copied between.

A FROM line can also name an earlier stage instead of a registry reference, as in FROM build AS test. The stage is then the base image, and the same inheritance rules apply.

How the reference resolves

The text after FROM is a reference, and the reference form decides how stable the build's starting point is across runs.

Reference formExampleWhat it resolves toStability between two builds
Floating tagnode:22Whatever the registry currently serves for that tagChanges whenever the maintainer republishes the tag
Pinned digestnode@sha256:<digest>Exactly one manifest, by content addressFixed, and a mismatch fails the pull
Tag plus digestnode:22@sha256:<digest>The digest, with the tag kept as a readable hintFixed, and the tag documents the intent
Earlier stagebuildA stage defined higher in the same DockerfileFixed within the build

A tag on a public image such as node:22 is a moving pointer by design: the maintainers republish it when the patch release, the distribution packages, or the security fixes underneath it change. The digest is the content address of the manifest and identifies one exact image.

Multi-platform references add a second step. A tag commonly points at an image index that lists one manifest per platform, so the digest a build resolves depends on the target platform. docker buildx imagetools inspect node:22 prints the index and the per-platform digests underneath it, which is the fastest way to see what a FROM line resolves to right now.

Example

This Dockerfile builds a Node application on a slim base image. Nothing here is unusual, and that is the point: the only reference to the outside world is on line 3.

# syntax=docker/dockerfile:1

FROM node:22-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
    && rm -rf /var/lib/apt/lists/*
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]

The workflow below builds it on a GitHub Actions runner and keeps a registry cache so that repeat builds reuse layers.

name: image
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: warp-ubuntu-latest-x64-2x
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          pull: true
          tags: ghcr.io/${{ github.repository }}:latest
          cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache
          cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max

What happens when the tag moves

Assume Monday's run pushed the image and populated the cache. On Tuesday the Node maintainers republish node:22-slim with a patched distribution package, so the tag now resolves to a different digest. Wednesday's push changes one file under src/.

BuildKit computes a cache key for each step from the step itself and the key of the step before it, so an invalidated step invalidates everything downstream of it (Docker build cache invalidation, checked on 2026-08-13). The base image is the parent of the first instruction, which puts it at the head of that chain.

InstructionMondayWednesday, after the tag moved
FROM node:22-slimPulledNew digest, so the chain restarts here
WORKDIR /appCachedRe-executed
RUN apt-get update && apt-get install ...CachedRe-executed, downloading package lists again
COPY package.json package-lock.json ./CachedRe-executed
RUN npm ci --omit=devCachedRe-executed, downloading the dependency set again
COPY . .CachedRe-executed, and the source changed anyway

The apt-get and npm ci steps are the expensive ones, and neither was touched by the commit. A registry cache does not help, because the cache entries recorded on Monday were keyed against the old parent digest and no key from Wednesday matches them.

The pull: true input in the workflow above is what makes the update land on the same day it is published. It maps to the --pull behavior of the build command and forces the builder to re-resolve every referenced tag instead of reusing an image already in its local store. Dropping it produces the mirror-image problem: a long-lived builder keeps serving a stale digest, so the workflow reports green while the published image still carries the unpatched packages.

Pinning turns the moving part into a reviewable one:

# <digest> comes from: docker buildx imagetools inspect node:22-slim
FROM node:22-slim@sha256:<digest>

The build now resolves the same manifest on every run until a commit changes the digest, so a rebuild triggered by an unrelated push cannot pick up a different starting point. The tradeoff is that patches stop arriving on their own and need an update path of their own, which is what the base image update workflow guide sets up on a schedule.

FAQ

What is a base image in a Dockerfile?

The base image is the image named in a FROM instruction. It supplies the root filesystem and the image configuration that the instructions after it build on, so the tools, libraries, default user, and PATH available to the first RUN line all come from it.

Does every Dockerfile have exactly one base image?

A Dockerfile has one base image per stage, and each FROM instruction starts a stage. A two stage Dockerfile that compiles in one image and copies the result into a smaller runtime image names two base images, and only the last stage decides what the published image contains.

Why does a base image update invalidate the rest of the build cache?

Each step's cache key includes the parent it was built on, so a new digest behind the same tag changes the parent of the first instruction and every key downstream of it. The build then re-executes every instruction after FROM even when the Dockerfile and the source tree are unchanged.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.