🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

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.

DevOpsBoysJun 6, 20264 min read
Share:Tweet

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

bash
# 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/sh

Failure 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:

dockerfile
# 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 install

For CI network issues:

bash
# 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:

dockerfile
# 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/html

Debug by checking what the stage actually built:

bash
docker build --target builder -t debug-builder .
docker run --rm debug-builder ls /app/dist

Failure 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:

dockerfile
# 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/html

Failure 4: Secret leaks in intermediate layer

Symptom: Build succeeds but security scan finds credentials in image layers.

Fix with BuildKit secrets:

dockerfile
# 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
bash
# 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:

dockerfile
# 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:

bash
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:

dockerfile
# 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 build

Failure 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:

bash
# Check .dockerignore
cat .dockerignore | grep config
 
# Verify files exist before build in CI
ls -la ./config
 
# Check if gitignored
git check-ignore -v config/
dockerignore
# If config/ was accidentally ignored, remove it or use negation
!config/

Debug Template

When a stage fails and you can't tell why:

bash
# 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> --version

Multi-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

Browse fixes
Newsletter

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

Comments