You run docker run -d or docker compose up -d, and a moment later the container shows Exited or keeps flipping to Restarting. A container lives exactly as long as its main process. When that process ends, for any reason, the container stops. This guide shows how to find the reason in a minute and fixes the usual causes.
Before you start
- A Linux VPS with Docker Engine and Compose.
- The name of the failing container. List all containers, including stopped ones:
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'A status such as Exited (1) 5 seconds ago or Restarting (127) 2 seconds ago tells you it has stopped and with which exit code.
Step 1: read the logs
The answer is usually in the last lines the app printed:
docker logs --tail 50 myappWith Compose:
docker compose logs --tail 50 appCommon messages and their meaning:
Error: missing required environment variable DATABASE_URL: configuration is missing.exec /app/start.sh: no such file or directory: wrong path or Windows line endings in a script.exec format error: the image was built for a different CPU architecture.ECONNREFUSEDorcould not connect to server: the app gives up because a dependency is not ready.
Step 2: check the exit code
docker inspect myapp --format 'ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}} Error={{.State.Error}}'What the codes mean:
- 0: the process finished normally. The command ran to completion, so there was nothing to keep the container alive.
- 1 or another small number: the app hit an error. The logs explain which.
- 125: Docker itself failed to run the container, often a bad option.
- 126: the command was found but cannot be executed, usually a permission problem.
- 127: the command was not found inside the image.
- 137: the process was killed, often by the out-of-memory killer. Check
OOMKilled. - 139: a segmentation fault inside the app.
Cause: missing environment variables
Many images need certain variables to start. For example, the official PostgreSQL image refuses to start without a password:
docker run -d --name db -e POSTGRES_PASSWORD=change-me postgres:16Read the image's documentation on Docker Hub for required variables. In Compose, check that an env_file path is right and that the file is in the same folder as compose.yaml if you use a relative path. docker compose config shows the final values Compose will use.
Cause: the command finishes or is wrong
A container needs a process that keeps running in the foreground. These do not:
- An image such as
ubuntuoralpinewith no command. Its default is a shell, which exits at once without a terminal. - A command that starts a service in the background, such as
service nginx start, then returns. - A script that ends after setup.
Run servers in the foreground. For Nginx, the official image already uses nginx -g 'daemon off;'. For your own images, make the last step of a start script use exec so the app replaces the shell:
exec node server.jsTo keep a test container alive while you investigate, override the command:
docker run -d --name debug myapp:1.0 sleep infinity
docker exec -it debug shInside, run the real start command by hand and watch the error appear.
Cause: script problems
no such file or directoryfor a script that exists: the file has Windows (CRLF) line endings, so the#!/bin/shline is wrong. Convert it withdos2unixor set your editor to LF.permission deniedrunning a script: make it executable before building, or addRUN chmod +x /app/start.shin the Dockerfile./bin/bash: not found: Alpine images have no bash. Use/bin/sh.
Cause: a dependency is not ready
Apps often exit if the database is still starting. Add a health check to the database and make the app wait for it:
services:
app:
depends_on:
db:
condition: service_healthyAdd health checks to Docker containers shows the matching database checks.
Restart loops
With restart: unless-stopped or always, a crashing container restarts again and again, with a growing delay. The cause is the same as above, but logs can scroll by. Stop the loop while you debug:
docker update --restart no myapp
docker stop myappFix the problem, then set the policy back with docker update --restart unless-stopped myapp or recreate it with Compose.
Check it works
After your fix, start the container and watch it for longer than ten seconds:
docker compose up -d
docker compose ps
docker compose logs -f --tail 20 appThe status should stay Up and the logs should show the app listening or ready, with no new errors.
Troubleshooting
- Logs are empty: the process fails before printing anything. Run the image interactively with
docker run --rm -it --entrypoint sh myapp:1.0and start it by hand. - Exit code 137 with
OOMKilled=true: raise the memory limit or reduce the app's memory use. See Limit Docker container CPU and memory. exec format error: the image does not match the server's CPU. See Fix "exec format error" and image platform mismatches.- Works with
docker runbut not in Compose: compare settings withdocker compose config. A differentcommand,useror missing volume is common.
Next steps
- Learn interactive debugging: Open a shell inside a running Docker container.
