docker container ls finds the running or stopped containers you actually mean, then formats them for scripts too. You will finish with a small set of commands for narrowing the result safely and producing output scripts can consume. The examples use Docker CE CLI 29.8.1, installed here as package version 5:29.8.1-1~ubuntu.24.04~noble.
Allow about ten minutes. You need a shell and access to a Docker daemon. Listing is normally an ordinary operation, but access to the daemon socket is privileged on many Linux systems. These commands inspect state only: they do not create, stop, restart or remove containers.
Confirm which client is first in your path, then run the command without options:
$ docker --version
Docker version 29.8.1, build 4a63305
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 web:latest "webd" 18 hours ago Up 18 hours 127.0.0.1:29630->8080/tcp web
Your rows will differ. The default is the first trap: docker container ls shows only containers that are running. A stopped container is not missing; it is outside this default view. docker container list and docker ps are aliases for the same operation, but the longer form makes the object being listed clear.
Checkpoint: if the command reports that it cannot connect to the daemon, check the daemon address and your permission to use the Docker socket. Do not immediately add sudo to scripts. Decide whether your local Docker access policy permits it, and keep the same client configuration when you test again.
Add --all, or its short form -a, when the question is about every container, not only current processes:
$ docker container ls --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 web:latest "webd" 18 hours ago Up 18 hours 127.0.0.1:29630->8080/tcp web
91e0f9c4c3aa example:1.2 "/start" 2 days ago Exited (1) 2 days ago example-old
Use this view after a failed deployment or an unexpected restart. The STATUS column includes useful detail such as an exit code, but it is formatted for people. For a precise exit-code search, filter with exited:
$ docker container ls --all --filter 'exited=1'
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
91e0f9c4c3aa example:1.2 "/start" 2 days ago Exited (1) 2 days ago example-old
The exited filter is useful with --all. Without it, a container that has already stopped cannot appear in the default running-only result.
Filters use key=value. Pass --filter more than once when you need more than one condition:
$ docker container ls --all \
--filter 'name=web' \
--filter 'status=running'
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6b7f2a1e90d4 example:web "/run" 3 hours ago Up 3 hours web-api
Name matching accepts part of a container name, so name=web can match more than one result. Review the output before using it in a follow-up command. Status values include created, running, paused, restarting, removing and exited; the current Docker documentation also lists dead.
Labels are a better boundary for automation because they express why a container belongs to a group:
$ docker container ls --all --filter 'label=com.example.owner=platform'
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6b7f2a1e90d4 example:web "/run" 3 hours ago Up 3 hours web-api
Use label=KEY to match the presence of a label, or label=KEY=VALUE to require its value. Quote the complete filter so a shell cannot reinterpret characters in a value supplied by another person or system.
Use resource filters when you know what the container exposes or mounts:
$ docker container ls --all --filter 'publish=443/tcp'
$ docker container ls --all --filter 'volume=/data'
$ docker container ls --all --filter 'network=frontend'
publish finds published ports, while expose finds exposed ports. Include /tcp or /udp when protocol matters; TCP is the default when the protocol is omitted. A port range is written like 8000-8080/tcp. The volume filter accepts a volume name or a mount path, and network accepts a network name or ID.
These filters answer different questions. A published port is reachable through a host-side mapping, whereas an exposed port is metadata about the container. Do not treat an exposed port as proof that a service is reachable from outside the container.
Use --format when the default wide table contains more detail than you need. A Go template without the table directive has no header:
$ docker container ls --format '{{.ID}} {{.Names}} {{.Status}}'
a1b2c3d4e5f6 web Up 18 hours
9f8e7d6c5b4a webhooks.example.com Up 20 hours (healthy)
For a readable custom table, declare the columns explicitly and use \t between them:
$ docker container ls --format 'table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}'
CONTAINER ID IMAGE STATUS NAMES
a1b2c3d4e5f6 web:latest Up 18 hours web
Useful placeholders include .ID, .Image, .Command, .CreatedAt, .RunningFor, .Ports, .State, .Status, .Names, .Labels, .Mounts and .Networks. Use .Label "KEY" when you need one label rather than the complete label string.
For machine-readable output, request JSON:
$ docker container ls --format json
{"Command":"\"/webd\"","ID":"a1b2c3d4e5f6","Image":"web:latest","Names":"web","State":"running","Status":"Up 18 hours"}
Do not parse the human table with awk and assume its spacing is an interface. Prefer a template or JSON, and treat fields such as status text as display data rather than a stable API contract.
When another command needs container identifiers, --quiet prints only IDs:
$ docker container ls --all --filter 'status=exited' --quiet
91e0f9c4c3aa
2d6b0c2a6f11
This is safe as an inspection step, but be careful before piping IDs into a command that changes state. A pipeline such as docker container ls -aq | xargs docker rm can remove every stopped container, including one you still need for logs or evidence. It is intentionally not used here. Confirm exact names or IDs and check the command's manual before any stop, restart or removal operation.
Use --latest to show the newest created container, or --last N for the last N created containers. Both include all states. These options describe creation order, not start order, so they are not a substitute for a timestamped deployment record. Add --no-trunc when a full ID or complete command is needed, and --size when you need each container's writable-layer and virtual-size information. Size inspection can be slower on a busy host.
--all and narrow the result with filters.