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:
- 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.
- 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 form | Example | What it resolves to | Stability between two builds |
|---|---|---|---|
| Floating tag | node:22 | Whatever the registry currently serves for that tag | Changes whenever the maintainer republishes the tag |
| Pinned digest | node@sha256:<digest> | Exactly one manifest, by content address | Fixed, and a mismatch fails the pull |
| Tag plus digest | node:22@sha256:<digest> | The digest, with the tag kept as a readable hint | Fixed, and the tag documents the intent |
| Earlier stage | build | A stage defined higher in the same Dockerfile | Fixed 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=maxWhat 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.
| Instruction | Monday | Wednesday, after the tag moved |
|---|---|---|
FROM node:22-slim | Pulled | New digest, so the chain restarts here |
WORKDIR /app | Cached | Re-executed |
RUN apt-get update && apt-get install ... | Cached | Re-executed, downloading package lists again |
COPY package.json package-lock.json ./ | Cached | Re-executed |
RUN npm ci --omit=dev | Cached | Re-executed, downloading the dependency set again |
COPY . . | Cached | Re-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.
Related Terms
- Automating base image updates in a GitHub Actions workflow: a scheduled workflow that checks for a new digest behind a pinned reference and opens a pull request with the bump.
- Multi stage builds and the base image each stage starts from: why a Dockerfile has one base image per
FROMline and how only the last stage reaches the published image. - Why a base image update invalidates a Docker build cache: the cache key chain behind the table above, and the steps that pay for it.
- Dockerfile reference for FROM: the upstream definition of the instruction, its
ASform, and thescratchreserved name. - Docker build cache invalidation: the rules that decide when a step is reused and when it runs again.
- WarpBuild Docker builders documentation: builder profiles and the actions that point a workflow at a builder.
- WarpBuild caching documentation: cache usage inside GitHub Actions workflows.
- WarpBuild pricing: per minute rates by runner type.
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.