Run Your First Containers: docker run, ps, logs, exec, stop, rm

You have Docker installed and hello-world printed its greeting. That container did its one job and exited immediately, which is not very interesting. Real containers stay up, serve traffic, and need starting, inspecting, and cleaning up. This post walks the everyday container lifecycle on a single command you will use constantly: a web server you can actually open.
By the end you will be able to start a container, see it running, talk to it, look inside it, stop and restart it, and remove it, all with a handful of docker commands. Every command and its output below was captured on a real Ubuntu 24.04 machine running Docker Engine 29.8.0.
Tip
docker run creates and starts a container. -d runs it in the background, -p publishes a port, --name gives it a friendly name.docker ps lists running containers, docker ps -a includes stopped ones.docker logs, docker exec, docker stop, docker start, and docker rm are the rest of the daily toolkit.--rm auto-deletes a container when it exits, which keeps throwaway runs from piling up.Prerequisites
- Docker installed and working. If
docker run hello-worldprints "Hello from Docker!", you are set. If not, start with Install Docker on macOS, Windows (WSL2), and Linux. - On Linux, either be in the
dockergroup or prefix these commands withsudo. The install guide covers the group step.
Images versus containers, in one minute
This is the one idea that makes everything else click. An image is a packaged, read-only snapshot: an application plus everything it needs to run. A container is a live, running copy created from that image, with its own writable layer on top. You can start ten containers from the same image, and each one is independent.
When you run docker run nginx, Docker looks for the nginx image locally, pulls it from Docker Hub (the default public registry, home to thousands of ready to run images) if it is missing, then creates and starts a container from it. Let us do exactly that, but with a real web server you can open in a browser.
Start a container
Run the official nginx web server, in the background, with its port published to your machine:
1docker run -d -p 8080:80 --name web nginxThree flags are doing the work here. -d (detached) runs the container in the background and hands you back your prompt. -p 8080:80 maps port 8080 on your machine to port 80 inside the container, where nginx listens. --name web gives the container a name so you do not have to use its ID for every later command.
On the first run, Docker pulls the image, then prints the new container's full ID:
1Status: Downloaded newer image for nginx:latest222bc7448b0bc864f0fbe6fce4966fa99a07ca7e017a11d6bcdadf5d738f7cb51See it running
1docker psdocker ps lists running containers. Here is the row for the one we just started (columns trimmed to fit):
1CONTAINER ID IMAGE STATUS PORTS NAMES222bc7448b0bc nginx Up Less than a second 0.0.0.0:8080->80/tcp webThat 0.0.0.0:8080->80/tcp is the port mapping in action. Note the short container ID (22bc7448b0bc); it is just the first 12 characters of the full ID from the run command.
Talk to it
The container is serving nginx on port 8080. Ask it for a page:
1curl -s localhost:8080 | grep -i title1<title>Welcome to nginx!</title>That is a real HTTP response from software running inside the container. Open http://localhost:8080 in a browser and you will see the nginx welcome page.
Look inside
Two commands cover most of what you need to inspect a running container.
Read its logs with docker logs. nginx writes each request to its access log, so the curl above shows up:
1docker logs web1172.17.0.1 - - [10/Sep/2026:10:12:07 +0000] "GET / HTTP/1.1" 200 896 "-" "curl/8.5.0" "-"Get a shell inside with docker exec. The -it flags give you an interactive terminal:
1docker exec -it web shNow you are inside the container. Poke around, then type exit to leave. For a quick one-off you can also run a single command without an interactive shell:
1docker exec web whoami2docker exec web nginx -v1root2nginx version: nginx/1.31.5Info
docker run (create a new container) and docker exec (step into an existing one), which trips up a lot of beginners.Stop, start, and see stopped containers
Stop the container. Docker asks nginx to shut down gracefully and returns the name once it has:
1docker stop web1webA stopped container does not vanish. It still exists, just not running. docker ps alone will not show it, but docker ps -a will:
1docker ps -a1NAMES STATUS2web Exited (0)Exited (0) means it stopped cleanly (exit code 0). Because the container still exists, you can start it again without recreating it, and it keeps its name and configuration:
1docker start web2docker ps1NAMES STATUS PORTS2web Up Less than a second 0.0.0.0:8080->80/tcpClean up
When you are done, remove the container. A running container will not be removed unless you force it, so -f stops and removes in one step:
1docker rm -f web1webNow docker ps -a no longer lists web. The container is gone; the nginx image stays cached locally for next time.
Warning
docker run without --rm leaves a container behind after it exits, and they accumulate quietly. Run docker ps -a now and then to see them, and remove the ones you do not need.For anything short-lived, skip the cleanup entirely with --rm, which deletes the container the moment it exits. This runs a tiny Alpine Linux container, prints one line, and removes itself:
1docker run --rm alpine echo "hello from a throwaway container"1hello from a throwaway containerAfter that, docker ps -a shows nothing new. The container ran and cleaned up after itself. This is the pattern for one-off commands and scripts.
The lifecycle at a glance
That is the whole loop you will use every day:
docker runcreates and starts a container (add-d,-p,--name,--rmas needed).docker ps/docker ps -alist running / all containers.docker logsreads its output.docker exec -it <name> shopens a shell inside it.docker stop/docker startpause and resume it.docker rm -fremoves it.
Common gotchas
"port is already allocated"
Something else is using host port 8080. Either stop that process or publish a different port, for example -p 8081:80, then browse to 8081.
docker exec says the container is not running
exec only works on a running container. Check docker ps -a; if it shows Exited, run docker start <name> first.
The name is already in use
Container names are unique. If docker run --name web fails because web exists (even stopped), remove the old one with docker rm web or pick a new name.
Changes inside a container disappear
Anything you write inside a container is lost when it is removed. That is by design. Persisting data needs volumes, which is a later topic in this series.
Where to go next
You can now drive a single container through its whole life. Real applications are rarely one container though; they are an app plus a database plus a cache, wired together. That is what Docker Compose is for.
- Next in this series: Docker Compose, where we bring up a web app, Postgres, and Redis together with one
docker compose up.
Verified on 2026-09-10 on a real Ubuntu 24.04.5 LTS system running Docker Engine 29.8.0. Every command in this post was run in order against the official `nginx` image (container `22bc7448b0bc`, nginx 1.31.5) and the `alpine` image: `docker run -d -p 8080:80 --name web nginx`, `docker ps`, `curl localhost:8080` returning the nginx welcome page, `docker logs` showing the captured `GET / 200`, `docker exec` returning `root` and the nginx version, `docker stop` (Exited 0), `docker start`, `docker rm -f`, and a `--rm` throwaway run.
Join the discussion on Run Your First Containers: docker run, ps, logs, exec, stop, rm
Likes, comments, and replies are available for authenticated readers with verified email addresses.


