Docker exit code 128 almost always comes from a failed git operation (auth, host-key, or directory-ownership) during a build, or from the OCI runtime failing to create the container. Here is how to diagnose and fix it.
In the Docker world, "exit code 128" is reported in two distinct situations, and it is important not to confuse them. **1. A command inside a build or container returned 128.** This is by far the most common case, and the command is nearly always `git`. Git itself uses exit code 128 as its generic "fatal" exit status, so any fatal git error — authentication failure, "Host key verification failed", "repository not found", or "detected dubious ownership in repository" — surfaces as `exit status 128` (often shown as `The command '/bin/sh -c git clone ...' returned a non-zero code: 128` during `docker build`). The number does not tell you which git problem occurred; you have to read the log line above it. **2. The OCI runtime (runc/crun via containerd) failed to create or start the container.** Here Docker reports something like `OCI runtime create failed: ...` and the container never runs your process. This is a configuration or environment problem — a bad entrypoint path, a missing binary, a permission problem, or a runtime/containerd issue — not a signal and not your application's own exit code. What 128 does *not* mean for Docker: it is not a "signal exit." Signal-terminated processes report `128 + n` (e.g. 137 for SIGKILL, 143 for SIGTERM), so 128 itself is not in that family. And while bash returns 128 for an out-of-range/non-integer argument to its `exit` builtin, that is a niche scripting edge case and is rarely the real reason a Docker build or container reports 128 — don't go looking there first.
Exit code 128 is generic — the real cause is in the line just before it. Don't guess; read the log.
For a build failure, re-read the failing step's output (look for fatal: from git). For a stopped container:
# Application output (if any)
docker logs <container_name_or_id>
# Exit code and any runtime-level error message
docker inspect <container_name_or_id> --format='{{.State.ExitCode}} {{.State.Error}}'Key strings to look for: Authentication failed, Host key verification failed, repository not found, detected dubious ownership, or OCI runtime create failed. Each points to a different fix below.
If the log shows a git auth failure, supply credentials to the build without baking secrets into image layers.
Preferred: BuildKit SSH forwarding — the key is never written into the image.
# syntax=docker/dockerfile:1
FROM alpine
RUN apk add --no-cache git openssh-client
# Trust GitHub's host key (avoids 'Host key verification failed')
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh git clone [email protected]:user/repo.gitBuild with your SSH agent forwarded in:
DOCKER_BUILDKIT=1 docker build --ssh default .Alternative for HTTPS: a build secret (also keeps the token out of the final image, unlike an ARG):
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=gh_token \
git clone https://x-access-token:$(cat /run/secrets/gh_token)@github.com/user/repo.gitDOCKER_BUILDKIT=1 docker build --secret id=gh_token,env=GITHUB_TOKEN .Avoid passing the token via a plain ARG/ENV — it gets stored in image history and leaks the credential.
If the error is Host key verification failed, the build environment has never connected to the host and won't accept an unknown key non-interactively. Add the host key explicitly:
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hostsDo not disable host-key checking (e.g. StrictHostKeyChecking=no or UserKnownHostsFile=/dev/null) — that defeats the protection against man-in-the-middle attacks. ssh-keyscan pinning the known host is the safe equivalent. For extra assurance, verify the scanned fingerprint against the provider's published keys.
If the log shows fatal: detected dubious ownership in repository at '/app', git is refusing to operate because the working tree is owned by a different user than the one running git (common when source is bind-mounted and the container runs as root).
Mark the specific path as safe:
git config --global --add safe.directory /appOr in a Dockerfile, scoped to the known path:
RUN git config --global --add safe.directory /appPrefer naming the exact directory. Using the wildcard safe.directory '*' disables this safety check everywhere and should only be a last resort in a fully trusted, single-purpose image.
If git reports repository not found or a missing ref, 128 is just git's fatal status for a bad target. Confirm the clone target outside Docker first:
git ls-remote [email protected]:user/repo.git
# or for HTTPS
git ls-remote https://github.com/user/repo.gitCheck for typos in the URL, a private repo accessed without credentials, or a --branch/ref that no longer exists.
If Docker reports OCI runtime create failed rather than a git error, the runtime couldn't start your process. Common root causes: an entrypoint/command that doesn't exist or isn't executable, a wrong path, or a missing interpreter (e.g. a #!/bin/bash script in an image that only has /bin/sh).
Inspect what the image is trying to run, then test it interactively:
docker inspect <image> --format='ENTRYPOINT={{.Config.Entrypoint}} CMD={{.Config.Cmd}}'
# Override the entrypoint to get a shell and probe the environment
docker run -it --entrypoint /bin/sh <image>
ls -la /app/entrypoint.sh # exists and executable (x bit)?
head -1 /app/entrypoint.sh # correct shebang for an interpreter present in the image?Fix the path/permissions (chmod +x), correct the shebang, or install the missing interpreter.
If the image is fine but OCI runtime create failed persists across containers, check the daemon and runtime on the host:
sudo journalctl -u docker.service -n 50
sudo journalctl -u containerd.service -n 50
docker version
containerd --version
runc --versionA restart often clears transient runtime state:
sudo systemctl restart dockerIf versions are mismatched, update via your package manager (e.g. sudo apt-get update && sudo apt-get install --only-upgrade containerd.io docker-ce).
Note: docker system prune deletes all stopped containers, unused networks, dangling images, and the build cache — it is not a targeted fix and can remove data you still need. Only run it after confirming you don't need those resources, and avoid prune --volumes unless you are certain the data is disposable.
128 is not a signal exit. Signal-terminated containers report 128 + n (e.g. 130 = SIGINT, 137 = SIGKILL/OOM, 139 = SIGSEGV, 143 = SIGTERM). A bare 128 is *not* in that family — it is overwhelmingly git's generic fatal status or an OCI runtime create failure. Don't apply the 128+n reasoning to a plain 128.
Why git returns 128: git uses exit code 128 as its catch-all "fatal" status (die() internally). That is why authentication, host-key, ownership, and "not found" errors all collapse to the same number — the human-readable fatal: line is the real diagnostic, not the code.
BuildKit SSH agent setup: for --mount=type=ssh, your agent must hold the key on the host before building:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
DOCKER_BUILDKIT=1 docker build --ssh default .The key stays on the host and is forwarded only for that RUN step, so it never lands in an image layer.
Don't weaken transport security to silence 128: disabling StrictHostKeyChecking or embedding tokens via ARG/ENV will make the error go away but introduces a MITM or credential-leak risk. Use ssh-keyscan to pin known hosts and BuildKit --ssh/--secret to keep credentials out of the image.
Systemd ordering for network-dependent containers: if a container relies on a network mount at boot, order the unit after the mount is ready:
# /etc/systemd/system/docker-myapp.service
[Unit]
After=remote-fs.target docker.service
Requires=docker.serviceError response from daemon: Get https://registry-1.docker.io/v2/: Proxy Authentication Required
How to fix 'Proxy Authentication Required' in Docker
error exporting cache: failed to export cache
How to fix 'error exporting cache: failed to export cache' in Docker
net/http: TLS handshake timeout
How to fix 'net/http: TLS handshake timeout' in Docker
Docker container exited with code 255
How to fix 'Container exited with code 255' in Docker
Read-only file system
How to fix 'Read-only file system' in Docker