Multi-Stage Docker Build Failing at Specific Stage — Fix
Multi-stage Docker builds failing mid-build? Fix RUN cache misses, COPY path errors, wrong base image, build args not passing between stages, and secret leaking in intermediate layers.
Multi-stage Docker builds fail in ways that are hard to debug because Docker's error output points to the failing line but not why. These are the most common failure patterns and exact fixes.
Diagnosing Which Stage Failed
# Build with verbose output
docker build --progress=plain -t myapp . 2>&1 | tee build.log
# Target a specific stage to isolate the failure
docker build --target builder -t myapp-debug .
# Inspect the failing intermediate image
docker run -it myapp-debug /bin/shFailure 1: RUN command fails but works locally
Symptom:
#8 [builder 3/5] RUN npm install
#8 ERROR: process "/bin/sh -c npm install" did not complete successfully: exit code 1
Causes:
- Missing build-time dependencies in the base image
- Network access blocked in CI
- Wrong user permissions
Fix:
# BAD — minimal node image missing build tools
FROM node:20-alpine AS builder
RUN npm install # fails: node-gyp needs python, make, g++
# GOOD — add build dependencies first
FROM node:20-alpine AS builder
RUN apk add --no-cache python3 make g++
RUN npm installFor CI network issues:
# Check if pip/npm can reach internet in CI
docker build --network=host -t myapp .
# or
docker build --build-arg HTTP_PROXY=$HTTP_PROXY -t myapp .Failure 2: COPY --from fails with "not found"
Symptom:
COPY --from=builder /app/dist ./dist
failed to solve: failed to read dockerfile: failed to copy: no source files were specified
Cause: The path doesn't exist in the source stage, OR the stage name is wrong.
Fix:
# BAD — typo in stage name
FROM node:20 AS builer # typo: "builer"
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html # "builder" != "builer"
# GOOD — names must match exactly
FROM node:20 AS builder
RUN npm run build && ls /app/dist # verify output exists
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/htmlDebug by checking what the stage actually built:
docker build --target builder -t debug-builder .
docker run --rm debug-builder ls /app/distFailure 3: Build ARGs not passing between stages
Symptom: Build arg is available in one stage but echo $ARG prints empty in the next.
Explanation: ARG values don't automatically carry across FROM instructions. Each stage resets them.
Fix:
# WRONG — ARG defined before FROM doesn't persist after it
ARG APP_VERSION=1.0.0
FROM node:20 AS builder
# $APP_VERSION is empty here!
# CORRECT — re-declare ARG in each stage that needs it
ARG APP_VERSION=1.0.0
FROM node:20 AS builder
ARG APP_VERSION # re-declare (inherits the top-level default)
RUN echo "Building version $APP_VERSION"
RUN npm run build
FROM nginx:alpine
ARG APP_VERSION # re-declare again if needed here
LABEL version="$APP_VERSION"
COPY --from=builder /app/dist /usr/share/nginx/htmlFailure 4: Secret leaks in intermediate layer
Symptom: Build succeeds but security scan finds credentials in image layers.
Fix with BuildKit secrets:
# BAD — secret ends up in layer history
FROM alpine AS builder
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
RUN npm install
# GOOD — secret never persists in layers
# syntax=docker/dockerfile:1
FROM alpine AS builder
RUN --mount=type=secret,id=npmrc,dst=/root/.npmrc \
npm install# Pass secret at build time
docker build \
--secret id=npmrc,src=$HOME/.npmrc \
-t myapp .Failure 5: Wrong platform in FROM image
Symptom on ARM Mac:
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8)
exec /app/server: exec format error
Fix:
# Explicit platform
FROM --platform=linux/amd64 golang:1.22 AS builder
# Or build for target platform
# Build for linux/amd64 on any host:
# docker build --platform linux/amd64 -t myapp .For multi-arch builds:
docker buildx create --use
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myregistry/myapp:latest \
--push .Failure 6: Layer cache invalidation causing slow/broken builds
Symptom: Build re-runs everything after an unrelated file changes.
Fix — order matters:
# BAD — copying everything first invalidates npm install cache
FROM node:20 AS builder
COPY . . # ANY file change re-runs npm install
RUN npm install
# GOOD — copy dependency files first
FROM node:20 AS builder
COPY package.json package-lock.json ./
RUN npm install # only re-runs if package.json changes
COPY . .
RUN npm run buildFailure 7: COPY fails in CI but works locally
Symptom: COPY ./config ./config fails in CI with "no files found."
Cause: .dockerignore includes the directory, or CI checkout doesn't include it.
Fix:
# Check .dockerignore
cat .dockerignore | grep config
# Verify files exist before build in CI
ls -la ./config
# Check if gitignored
git check-ignore -v config/# If config/ was accidentally ignored, remove it or use negation
!config/Debug Template
When a stage fails and you can't tell why:
# 1. Build up to the failing stage
docker build --target <stage-before-failing> -t debug-img .
# 2. Run interactively inside that stage
docker run --rm -it debug-img /bin/sh
# 3. Manually run the failing command
RUN npm install → npm install
# 4. Check what's available
ls, env, which <tool>, <tool> --versionMulti-stage build failures almost always come down to: path doesn't exist, dependency missing in base image, ARG scope reset, or platform mismatch. Isolate the stage first, then the command.
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Docker Build Fails in CI But Works Locally — Fix
Your Docker build works perfectly on your machine but fails in GitHub Actions, GitLab CI, or Jenkins. Here's every reason this happens and exactly how to fix it.
Docker Build Taking Too Long — Cache and Speed Fixes (2026)
Docker builds taking 10+ minutes every time? Here's how to fix layer caching, use BuildKit properly, and cut build times by 80% with multi-stage builds and cache mounts.
GitHub Actions Docker Push: Permission Denied / Unauthorized Fix (2026)
Getting 'permission denied' or 'unauthorized: authentication required' when pushing Docker images in GitHub Actions? Here are all the causes and fixes.