Fix: Docker container exits immediately with code 0
Docker container starts and stops in less than a second with exit code 0 — no error message. Here are the real causes and the fix for each.
You run:
docker run myapp
Container starts. Container stops. Zero output, exit code 0, nothing wrong per docker inspect. Just… gone. Here are the six real causes and the specific fix for each.
Diagnose first
Get actual state:
docker ps -a # see the exited container
docker logs <container-id> # see any output
docker inspect <container-id> # full detail — exit code, cmd, entrypoint
Check the effective command:
docker inspect <container-id> --format '{{.Config.Cmd}} / {{.Config.Entrypoint}}'
Exit code 0 means “container ran and finished normally”. So the real question is: what “finished normally” means for your container.
Cause 1 — Foreground command exited (nothing keeps container alive)
Docker containers stop the moment their main process (PID 1) exits. If your CMD is ["echo", "hello"], the container prints and stops.
Symptom: brand-new image, first run, immediately exits.
Fix — make PID 1 a long-running process:
For a web app, run the actual server:
CMD ["python", "app.py"] # ✅ blocks on server
Not:
CMD ["python", "-c", "print('hi')"] # ❌ exits after print
For debugging, override with a shell:
docker run -it myapp /bin/sh
This keeps the container alive as long as the shell has input.
Cause 2 — Web server daemonizes and exits
Apache, nginx, and some Node.js frameworks fork into the background by default. The container’s foreground process exits, so the container exits — while the actual daemon runs (until Docker kills it seconds later).
Symptom: logs show starting up then nothing. Container exits with 0.
Fix — run the server in foreground mode:
nginx:
CMD ["nginx", "-g", "daemon off;"]
Apache:
CMD ["apachectl", "-D", "FOREGROUND"]
PM2:
CMD ["pm2-runtime", "app.js"] # not `pm2 start` — that forks
The “container way” is always foreground. Docker treats stdout as the log; daemonizing hides it.
Cause 3 — Entrypoint script exits silently after prep work
Custom entrypoint scripts (entrypoint.sh) sometimes do setup, then forget to exec the actual server:
#!/bin/sh
# ❌ Broken — script finishes after configure
./configure --port=8080
The script exits, so PID 1 exits, so container exits.
Fix — always exec the main process at the end:
#!/bin/sh
./configure --port=8080
exec "$@" # runs whatever was passed as CMD
Then:
ENTRYPOINT ["./entrypoint.sh"]
CMD ["./my-server"]
exec replaces the shell process with the server, keeping it as PID 1 and inheriting signal handling.
Cause 4 — Alpine / distroless container missing an interpreter
You built a Go binary on Ubuntu with glibc, then copied to Alpine which has musl. Or you’re running a bash script in busybox which only has sh.
Symptom: container exits with 0 but nothing runs. Or docker logs shows no such file or directory (misleading — the file exists, but its interpreter doesn’t).
Fix — check the binary’s linkage:
docker run --rm -it myapp file /path/to/binary
docker run --rm -it myapp ldd /path/to/binary
If ldd says not a dynamic executable but the app crashes, it’s static and OK. If ldd lists shared libraries missing from the image, install them or switch to a compatible base.
Options:
- Build fully static:
CGO_ENABLED=0for Go,--target=x86_64-unknown-linux-muslfor Rust - Use compatible base: match Ubuntu build with
ubunturuntime, notalpine - Use
distroless/ccfor glibc binaries;distroless/staticfor fully static
Cause 5 — Shell script shebang mismatch
The script has #!/bin/bash but the container only has sh (Alpine, busybox, distroless).
Symptom: docker logs shows /bin/bash: not found and exit code 127 (not 0). But close enough to this class of issue.
Fix — one of:
- Change shebang to
#!/bin/shand use POSIX-only syntax - Install bash in the container:
RUN apk add --no-cache bash(Alpine) - Use bash-compatible shell script tools (
shellcheckcatches most bashisms)
Cause 6 — Missing —tty flag with interactive-only apps
Some apps (Python REPL, less, vim) exit immediately if stdin/stdout aren’t a TTY.
Symptom: works with docker run -it, fails with plain docker run.
Fix — allocate a TTY for interactive commands:
docker run -it myapp python
For non-interactive scripts, don’t use an interactive-only app as the entrypoint. Write a script that redirects to python -c or similar.
The universal exits-immediately debug flow
Every silent-exit case, run in order:
# 1. See the ACTUAL exit code + cmd/entrypoint
docker inspect <container> --format 'exit={{.State.ExitCode}} cmd={{.Config.Cmd}}'
# 2. Read all logs (even if empty)
docker logs <container> 2>&1
# 3. Run the image with an interactive shell to explore
docker run --rm -it --entrypoint /bin/sh myapp
# 4. Inside that shell, try running the intended command manually
./my-server
# (see what it does when NOT under Docker's normal PID 1 rules)
# 5. If a script, check for exec at the end
cat entrypoint.sh
Ninety percent of silent exits resolve when step 4 reveals the truth.
Prevention
For new Dockerfiles:
- CMD runs a long-lived foreground process — never
echo,ls, or something that returns - Always
execat the end of entrypoint scripts — inherits PID 1 correctly - Match glibc/musl between build and runtime bases — or build fully static
- HEALTHCHECK in the Dockerfile — orchestrators know when app is dead:
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ CMD wget -qO- http://localhost:8080/health || exit 1 docker run --rm --initin dev — properly handles zombie processes- CI smoke test —
docker run myapp &thensleep 5; docker ps | grep myapp— fail build if container isn’t still running
Bottom line
A container exiting with code 0 means “PID 1 finished its job.” The question is always “what was PID 1?” Read docker inspect to see the actual command, then run the image with --entrypoint /bin/sh to explore what should be happening. Ninety percent of silent exits are: foreground command returned immediately, daemonizing web server, entrypoint script forgot exec, or a binary/interpreter mismatch. Six specific fixes cover them all.
DevOps YAML Pack
36 production-ready configs — Kubernetes, Docker Compose, GitHub Actions, Terraform, Helm, Ansible. Every file heavily commented. Copy, paste, ship.
Get the pack — ₹499 →