Debug a failed Docker build
Read the first meaningful failure, inspect build inputs, and create a named debug stage instead of treating BuildKit cache IDs as runnable images.
Start with the failing operation and the output immediately above it. A build can fail during parsing, source transfer, image resolution, authentication, filesystem copying, or command execution—not only because a RUN command returned a nonzero status.
Capture readable output
docker build --progress=plain -t myapp:debug .
For an error such as an apt exit code 100, inspect the package-manager message that precedes the final summary. The code identifies a failure category; it does not establish whether the cause is a missing package, a repository error, or an unavailable network path.
Record the builder, target platform, context, Dockerfile, and build arguments used in CI and locally. Keep credentials and secret values out of diagnostic logs.
Test a cache hypothesis
docker build --progress=plain --no-cache --pull -t myapp:debug .
Use this when cached instruction results or a stale base reference are plausible factors. --no-cache bypasses instruction-result reuse; --pull checks referenced images. BuildKit cache mounts and external package caches may still contain data, and mutable dependencies can change independently.
Build an explicit inspection stage
BuildKit log identifiers and cache digests are not necessarily images that docker run can start. Instead, name a stage immediately before the operation you want to investigate:
FROM ubuntu:24.04 AS debug-base
WORKDIR /app
COPY . .
FROM debug-base AS app
RUN ./scripts/install-dependencies.sh
The project must provide that script. Build the earlier stage and open a shell:
docker build --target debug-base -t myapp:debug-base .
docker run --rm -it --entrypoint /bin/sh myapp:debug-base
With a separate Buildx builder, you may need --load to import the built image into the local image store. A runtime shell does not automatically reproduce build-time secret/cache mounts, network settings, ARG values, or platform emulation. Recreate the relevant conditions deliberately and move the verified fix back into the Dockerfile.
Inspect the actual inputs
| Symptom | Useful checks |
|---|---|
| COPY source missing | Context root, checkout contents, .dockerignore, file case |
| Registry or package download failure | Credentials, DNS, proxy, certificates, repository availability |
exec format error |
Requested platform, executable architecture, interpreter and emulation |
| Unknown instruction or parse failure | Syntax, frontend version, continuation and quoting |
| No space left | Builder storage, filesystem capacity, inodes and quotas |
Changing --platform alone does not install emulation or make an incompatible executable work. Clearing storage indiscriminately is likewise not a substitute for identifying the full filesystem.
FAQ
Why does CI fail when my machine succeeds?
Compare inputs and environment first: a local ignored file, warm dependency cache, different architecture, or missing CI credentials can explain the difference. A cache-free build is one diagnostic experiment, not a universal answer.
Can I stop at an earlier stage?
Yes, --target NAME selects that stage and its dependencies. If the failure is inside that stage, create an earlier boundary for inspection.
Can I check configuration without a full build?
Supported Buildx/Docker versions provide docker build --check . for build checks. It is not an execution test of every RUN command or application behavior, and frontend resolution may still require infrastructure. An external Dockerfile linter is another static check, with its own rule set.
See multi-stage builds, build checks, and Buildx build options.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan