Docker Error Guide: 'returned a non-zero code: 1' — Failing RUN Step in a Build
Fix 'returned a non-zero code: 1' in Docker: read the failing RUN command's real output, reproduce it interactively, and repair the package or script.
- #docker
- #troubleshooting
- #errors
- #build
Stuck on this Docker with AI error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
Each RUN instruction executes a command in an intermediate container. If that command exits with any non-zero status, Docker aborts the build and reports the failing shell line and its exit code:
The command '/bin/sh -c apt-get install -y libpq-dev' returned a non-zero code: 1
The BuildKit form points at the same failure with the step number:
ERROR: process "/bin/sh -c npm ci" did not complete successfully: exit code: 1
The exit code (1 here) is the command’s own status — the real cause is in the output above this line.
Symptoms
docker buildstops partway through with areturned a non-zero codeline naming aRUNcommand.- The build fails on
apt-get install,npm ci,pip install,go build, or a custom script. - The same command works on your host but fails inside the build.
- Re-running the build fails identically at the same step (deterministic) or intermittently (flaky network).
Common Root Causes
- A package/dependency install failed — stale apt cache, a missing repo, or an unavailable version.
- A missing file or path — the
RUNreferences something a priorCOPYdid not place. - Network failure during the step — the build could not reach a registry or mirror.
- A script genuinely returned non-zero — a failing test, lint, or compile step.
- Missing build dependency — headers/toolchain absent (e.g.
gcc,libpq-dev) for a native module. set -e/ pipefail semantics — a command in a chain failed and the shell propagated the error.
Diagnostic Workflow
Read the real error above the summary line — build with plain progress to see full output:
docker build --progress=plain -t myapp:1.4.2 . 2>&1 | tail -60
Disable cache to rule out a stale layer masking the failure:
docker build --no-cache --progress=plain -t myapp:1.4.2 .
Reproduce the failing step interactively from the last good layer. Note the image ID printed just before the failing step, then shell into it:
docker run --rm -it <last_good_layer_id> /bin/sh
# now run the failing command by hand and read its output
apt-get update && apt-get install -y libpq-dev
For apt failures specifically, confirm the cache is refreshed in the same layer:
grep -n 'apt-get' Dockerfile
Example Root Cause Analysis
A Debian-based image failed at The command '/bin/sh -c apt-get install -y libpq-dev' returned a non-zero code: 100, and a similar Python image failed with code 1 on the same package. Rebuilding with --progress=plain surfaced the real message above the summary:
E: Unable to locate package libpq-dev
The Dockerfile ran apt-get install without a preceding apt-get update in the same RUN, so the package index was empty and apt could not find the package. Because Docker caches layers, an earlier successful update in a separate RUN had been invalidated and not re-run.
The fix combined update and install in one RUN so the index is always fresh when installing:
RUN apt-get update && apt-get install -y --no-install-recommends libpq-dev \
&& rm -rf /var/lib/apt/lists/*
The build then completed. The principle: the summary line only names the command — the actionable error is always in the fuller output, which --progress=plain and interactive reproduction reveal.
Prevention Best Practices
- Always combine
apt-get update && apt-get installin a singleRUNso the package index is fresh with the install. - Pin versions where reproducibility matters and clean caches (
rm -rf /var/lib/apt/lists/*) to keep layers lean. - Build with
--progress=plainin CI so full step output is captured, not just the summary line. - Ensure every file a
RUNneeds isCOPYd before that step. - Reproduce failing steps interactively from the last good layer instead of guessing.
- Lint the Dockerfile with the free Dockerfile validator to catch ordering and install anti-patterns.
Quick Command Reference
docker build --progress=plain -t myapp:1.4.2 . 2>&1 | tail -60 # full output
docker build --no-cache --progress=plain -t myapp:1.4.2 . # rule out cache
docker run --rm -it <last_good_layer_id> /bin/sh # reproduce step
grep -n 'RUN' Dockerfile # review steps
Conclusion
returned a non-zero code: 1 simply reports that a RUN command failed — the exit code and command name are a pointer, not the cause. Rebuild with --progress=plain to read the real error, reproduce the step interactively from the last good layer, and fix the underlying package, path, or script problem. Combining apt-get update with installs and copying files before they are used prevents the most common cases. See more build fixes in the Docker guides.
Fixed it? Get 500 Docker with AI & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.