docker ps shows you running containers, and that word "running" is where most people trip. Run it after a container has crashed or exited and you get an empty table, even though the container is still sitting on disk waiting to be inspected or removed. The command mirrors the Unix ps tool, which also shows only live processes by default, so the behavior is intentional rather than a bug.
This article covers how to list containers in every state, filter the list down to what you care about, and format the output so it's useful in scripts and dashboards.
The basic command
The two commands you'll use are docker ps and its modern alias docker container ls. They're identical in behavior and support the same flags. Docker regrouped its CLI around the objects it manages back in version 1.13, which is where docker container ls came from, but docker ps never went away and is still what most people type.
To see what's currently running:
1docker ps
1234CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESc8bded53da86 postgres "docker-entrypoint.s…" 2 hours ago Up 2 hours 5432/tcp pg571c3a115fcf redis "docker-entrypoint.s…" 4 hours ago Up 4 hours 6379/tcp cache05ef6d8680ba nginx "nginx -g 'daemon of…" 2 hours ago Up 2 hours 80/tcp web
Each row is one container. The columns worth knowing are STATUS (whether it's up, and for how long, or when it exited), PORTS (the port mappings, which are empty for stopped containers), and NAMES (the human-readable name Docker assigns if you don't set one with --name).
Listing all containers, including stopped ones
To include stopped, exited, and created containers, add -a (or --all):
1docker ps -a
123456CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESc8bded53da86 postgres "docker-entrypoint.s…" 2 hours ago Up 2 hours 5432/tcp pg32928d81a65f mysql:8.4 "docker-entrypoint.s…" 8 hours ago Exited (1) 8 hours ago festive_bardeen571c3a115fcf redis "docker-entrypoint.s…" 4 hours ago Up 4 hours 6379/tcp cache05ef6d8680ba nginx "nginx -g 'daemon of…" 2 hours ago Up 2 hours 80/tcp web16d3c67ebd40 nginx "nginx -g 'daemon of…" 9 hours ago Exited (0) 9 hours ago old_web
Now you can see the containers that stopped alongside the ones still running. The STATUS column tells you how each one ended: Exited (0) means a clean shutdown, and any non-zero code means the process died with an error. That exit code is the single most useful piece of information here, and it's the first thing to look at when a container won't stay up.
If you only need the last few containers regardless of state, -n limits the list to the N most recently created ones, and -l (latest) shows just the last one. Both implicitly include stopped containers:
1docker ps -n 3
Filtering the list
On a busy host, docker ps -a returns dozens of rows and you have to eyeball the one you want. The --filter (or -f) flag narrows it down. It takes a key=value pair, and you can stack multiple --filter flags to combine conditions.
Filter by status to find every container that has exited:
1docker ps -a --filter status=exited
123CONTAINER ID IMAGE STATUS32928d81a65f mysql:8.4 Exited (1) 8 hours ago16d3c67ebd40 nginx Exited (0) 9 hours ago
The valid status values are created, restarting, running, removing, paused, exited, and dead. You can also filter by the exit code itself, which is where this gets useful for debugging. To find containers the kernel killed:
1docker ps -a --filter status=exited --filter exited=137
An exit code of 137 means the process received a SIGKILL (128 + signal 9). In practice that almost always means the container hit its memory limit and the Linux out-of-memory (OOM) killer terminated it, so this one filter is a fast way to spot memory problems across a host. To watch the resource usage that leads to those kills before they happen, see monitoring container resource usage with docker stats.
Other filters you'll reach for regularly: name matches on a substring of the container name, and ancestor matches every container started from a given image, which is handy when you want all the containers running a specific version:
1docker ps --filter ancestor=nginx:1.27
Formatting the output for scripts
The default table is fine for reading but awful for scripting, because the columns are truncated and space-aligned. The -q (quiet) flag strips everything down to container IDs, which is what you want when piping into another command:
1docker ps -q
123c8bded53da86571c3a115fcf05ef6d8680ba
This composes nicely with other Docker commands. To stop every running container, for example, you'd feed these IDs straight into docker stop:
1docker stop $(docker ps -q)
For anything more structured, --format takes a Go template so you can pick exactly the fields you want. Prefix the template with table to keep the header row:
1docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
1234NAMES STATUS IMAGEpg Up 2 hours postgrescache Up 4 hours redisweb Up 2 hours nginx
When you need machine-readable output, ask for JSON. Docker emits one JSON object per line (JSON Lines), which pairs well with jq:
1docker ps -a --format json | jq '{name: .Names, state: .State, status: .Status}'
This is the format to reach for when you're feeding container state into a monitoring script or a deployment check rather than reading it yourself.
A note on Docker Compose
If your containers are managed by Compose, docker ps still shows them, but it shows every container on the host mixed together. To scope the list to a single project, run docker compose ps from the directory holding your compose.yaml:
1docker compose ps
123NAME IMAGE COMMAND SERVICE CREATED STATUS PORTSweb nginx "nginx -g 'daemon of…" web 2 hours ago Up 2 hours 0.0.0.0:8080->80/tcpapi myapp:2.1 "node server.js" api 2 hours ago Up 2 hours 3000/tcp
It lists only the containers belonging to that project and adds a SERVICE column that maps each container back to its Compose service. Like docker ps, it hides stopped containers unless you pass --all.
Common pitfalls
The empty-list surprise is the one that catches everyone: you run docker ps, see nothing, and assume the container was never created, when it actually started, crashed, and exited in the time it took you to switch windows. Whenever a container seems to have vanished, run docker ps -a before concluding anything. The exited container is almost always still there with an exit code waiting to explain what happened.
The second trap is reading the STATUS string in scripts. It's a human-friendly sentence like Exited (137) 8 hours ago, and its wording has changed across Docker releases. Don't parse it. Use --format json and read the structured .State field (running, exited, and so on) instead, so your automation doesn't break the next time the phrasing shifts. Note that docker ps doesn't expose the numeric exit code as a structured field; when you need the exit code itself, read it from docker inspect with docker inspect <container> --format '{{.State.ExitCode}}'.
One more thing worth knowing: docker ps lists containers, not images. If you're actually trying to see what images exist rather than what's running from them, you want docker images, and for images sitting in a registry rather than on your machine, see how to list Docker images in a remote registry.
Final thoughts
docker ps and docker container ls are the same command, they show only running containers until you add -a, and the --filter and --format flags turn a wall of text into exactly the slice you need. Once you're comfortable filtering by exit code, spotting a crash loop or an OOM kill on a single host takes seconds.
The harder problem is that docker ps is a snapshot of one machine at one moment. A container that keeps restarting or gets OOM-killed every few hours won't show up unless you happen to run the command at the right time, and correlating that behavior across a fleet of hosts is not something the CLI was built for.
Dash0's infrastructure monitoring tracks container resource usage continuously, alongside real-time logs and distributed traces, so you can see which containers are consuming what and why without SSH-ing into each host to run docker ps. If you're orchestrating with Kubernetes, Kubernetes monitoring surfaces pod restarts and crash loops across the whole cluster. As an AI Control Plane for Production, Dash0 correlates that container telemetry with the rest of your stack in one place.
Start a free trial to monitor your containers, hosts, and clusters in a single view. No credit card required.