# Why Your Docker Container Exits Immediately (and How to Fix It)

URL: https://www.devopsness.com/blog/fix-docker-container-exits-immediately
Author: Kiril Urbonas
Published: 2026-07-09
Updated: 2026-10-03
Categories: DevOps
Tags: Docker, Containers, DevOps, Debugging, Operations

Summary: You run the container, check docker ps, and it's already gone. Nine times out of ten it's PID 1 doing its job. Here's how to read it.

---
# Why Your Docker Container Exits Immediately (and How to Fix It)

A Docker container exits immediately because its PID 1 process ended: the command finished, forked into the background, or crashed. Read the exit code with `docker ps -a`, read the reason with `docker logs`, and keep your main process in the foreground (for nginx, `daemon off;`).

You run `docker run my-image`, the prompt comes back, then you check:

```bash
docker ps -a
CONTAINER ID   IMAGE      STATUS                     NAMES
a1b2c3d4e5f6   my-image   Exited (0) 2 seconds ago   sleepy_hopper
```

Gone. The confusing part is that `Exited (0)` means *success*. Nothing crashed. The container did exactly what you told it to; that just took two seconds.

## The one rule that explains all of this

A container lives exactly as long as its PID 1. That's it. When you start a container, Docker runs your `CMD` (or `ENTRYPOINT`) as process ID 1 inside the namespace. When that process exits, the container stops. There is no background. There is no "keep it warm." PID 1 returns, the container is dead, exit code and all.

So the question is never "why did my container stop?" It's "why did PID 1 finish?"

## Cause 1: your main process actually finished

This is `Exited (0)` land. Your CMD ran a command that completes and returns.

```dockerfile
CMD ["python", "setup_db.py"]
```

That script sets up a schema and exits. Container's done its job, so it stops. Same thing if your CMD is `echo hello` or `ls` or a one-shot migration. People hit this constantly with base images: `docker run ubuntu` exits instantly because the default command is `bash`, and with no TTY attached `bash` has no input, reaches EOF, and returns.

The fix depends on intent. If it was supposed to be a one-shot job, congratulations, it worked. If you wanted a service, you need a process that stays in the foreground and doesn't return until you kill it:

```dockerfile
CMD ["python", "app.py"]        # app.py runs a server loop, blocks forever
```

## Cause 2: the app daemonizes itself

This one is sneaky because the process is a "server," but it forks a background copy and the foreground one exits. PID 1 returned, so the container dies even though the daemon technically started.

nginx is the classic. By default it backgrounds itself. You have to tell it not to:

```dockerfile
CMD ["nginx", "-g", "daemon off;"]
```

Apache wants `httpd -D FOREGROUND`. Anything you'd normally run with a `-d` flag or a `start` subcommand is suspect. The rule: never let a process inside a container background itself. Docker *is* your process manager now; don't reach for `systemd` inside a container to work around this.

## Cause 3: nonzero exit, meaning it crashed

`Exited (1)`, `Exited (127)`, `Exited (137)`. Now it's failing, and the answer is in the logs:

```bash
docker logs sleepy_hopper
docker inspect --format '{{.State.ExitCode}} OOMKilled={{.State.OOMKilled}}' sleepy_hopper
```

A few codes carry meaning on their own. Docker's run reference (checked October 2026) defines 125, 126, and 127; any other code is your process's own:

| Exit code | Meaning | First thing to check |
|---|---|---|
| 0 | PID 1 finished successfully | One-shot CMD or a daemonizing process |
| 1, 2 | Generic application error | `docker logs` |
| 125 | Error in the Docker daemon or `docker run` itself | Bad flag, name clash, invalid option |
| 126 | Command found but can't be invoked | Missing execute bit on the entrypoint script |
| 127 | Command not found | Typo in CMD, binary not in image, wrong `PATH` |
| 137 | 128 + 9, SIGKILL | `OOMKilled: true`, or a `docker stop` timeout |
| 139 | 128 + 11, SIGSEGV | Native crash, often an architecture or libc mismatch |

We've watched people rebuild an image five times chasing a `127` that one `docker logs` would have named. Read the logs first.

## Cause 4: wrong CMD or ENTRYPOINT, or missing a TTY

Sometimes the image is fine and the invocation is wrong. If you want an interactive shell, you have to actually attach one:

```bash
docker run -it ubuntu bash     # -i keeps stdin open, -t gives a tty
```

Without `-it`, `bash` sees no input and exits immediately. That `Exited (0)` from `docker run ubuntu` earlier was exactly this.

When you're not sure what the image even tries to run, override the entrypoint and poke around from the inside:

```bash
docker run -it --entrypoint sh my-image
# now you're in the container's filesystem, run the CMD by hand and watch it fail
```

This is the highest-value move on the list: you see the actual error instead of guessing at a container that's already gone.

## Cause 5: shell form vs exec form, and signals

This one doesn't kill the container on startup, it bites you later, but it belongs here because it's the same PID 1 story. Compare:

```dockerfile
CMD python app.py               # shell form: runs `/bin/sh -c "python app.py"`
CMD ["python", "app.py"]        # exec form: python is PID 1 directly
```

In shell form, PID 1 is `sh`, and your app is a child. Docker's Dockerfile reference is blunt: shell form runs as a subcommand of `/bin/sh -c`, "which does not pass signals." On `docker stop`, the `SIGTERM` never reaches your app. Your app never hears the shutdown, gets a hard `SIGKILL` ten seconds later, and you get ungraceful exits and `Exited (137)` on shutdown. Use exec form (the JSON array) so your process is PID 1 and receives signals directly. If your app genuinely needs to reap child processes, add a tiny init with `docker run --init` rather than reaching for a shell wrapper.

## The debugging loop, start to finish

When a container won't stay up, this is the order:

```bash
docker ps -a                    # exit code: 0 = finished, nonzero = crashed
docker logs <container>         # what it printed before dying
docker run --rm my-image        # run in the foreground, no -d, watch it live
docker run -it --entrypoint sh my-image   # go inside, run CMD by hand
```

Ninety percent get solved at step two. For the wider map of Docker failures, our [Docker troubleshooting](/blog/docker-error-troubleshooting-guide) guide covers the rest of the usual suspects.

## Questions people ask

### Why does a Docker container stop when its main process exits?

Because the container is that process. Docker starts your `ENTRYPOINT` or `CMD` as PID 1 in a new namespace, and when PID 1 returns, the namespace has nothing left to run, so the container stops with that process's exit code. Background children don't count. Keep the main process in the foreground and the container stays up.

### Why does docker run ubuntu exit immediately?

The `ubuntu` image's default command is `bash`. Started without a terminal, bash reads empty stdin, hits end-of-file, and exits with code 0, so the container stops at once. Run `docker run -it ubuntu bash` for an interactive shell, or give it a long-running command such as `sleep infinity` if you only need the container alive to `docker exec` into.

### How do I keep a Docker container running?

Run a process that never returns in the foreground: your server with its foreground flag (`nginx -g 'daemon off;'`, `httpd -D FOREGROUND`), declared in exec form so it is PID 1. Avoid `tail -f /dev/null` hacks in production images; they keep the container alive while the real service is dead, which hides failures from restart policies and health checks.

## The call we'd make

Before touching the Dockerfile, run `docker ps -a` and `docker logs`. Read the exit code as a sentence: `0` means your process finished, nonzero means it broke, `137` means something killed it. If it's a service that dies on `0`, you have a backgrounding or one-shot-command problem, so pin the process to the foreground. Use exec-form CMD so signals land where they should. And keep `docker run -it --entrypoint sh` in your muscle memory, because a container you can walk around inside is worth ten you can only stare at in `ps -a`.

## Sources

- [Docker Docs: Running containers, exit status](https://docs.docker.com/engine/containers/run/)
- [Docker Docs: Dockerfile reference, shell and exec form](https://docs.docker.com/reference/dockerfile/)
- [Docker Docs: docker container run, --init](https://docs.docker.com/reference/cli/docker/container/run/)

