DEV Community

Cover image for Run Your First Containers: docker run, ps, logs, exec, stop, rm
Shubham Sharma
Shubham Sharma

Posted on Originally published at techdevmantra.com

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

Key takeaways

  • An image is the read-only template. A container is a running instance of it. One image, many containers.
  • 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-world prints "Hello from Docker!", you are set. If not, start with Install Docker on macOS, Windows (WSL2), and Linux.
  • On Linux, either be in the docker group or prefix these commands with sudo. 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:

docker run -d -p 8080:80 --name web nginx
Enter fullscreen mode Exit fullscreen mode

Three 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:

Status: Downloaded newer image for nginx:latest
22bc7448b0bc864f0fbe6fce4966fa99a07ca7e017a11d6bcdadf5d738f7cb51
Enter fullscreen mode Exit fullscreen mode

See it running

docker ps
Enter fullscreen mode Exit fullscreen mode

docker ps lists running containers. Here is the row for the one we just started (columns trimmed to fit):

CONTAINER ID   IMAGE     STATUS         PORTS                    NAMES
22bc7448b0bc   nginx     Up Less than a second   0.0.0.0:8080->80/tcp     web
Enter fullscreen mode Exit fullscreen mode

That 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:

curl -s localhost:8080 | grep -i title
Enter fullscreen mode Exit fullscreen mode
<title>Welcome to nginx!</title>
Enter fullscreen mode Exit fullscreen mode

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:

docker logs web
Enter fullscreen mode Exit fullscreen mode
172.17.0.1 - - [10/Sep/2026:10:12:07 +0000] "GET / HTTP/1.1" 200 896 "-" "curl/8.5.0" "-"
Enter fullscreen mode Exit fullscreen mode

Get a shell inside with docker exec. The -it flags give you an interactive terminal:

docker exec -it web sh
Enter fullscreen mode Exit fullscreen mode

Now 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:

docker exec web whoami
docker exec web nginx -v
Enter fullscreen mode Exit fullscreen mode
root
nginx version: nginx/1.31.5
Enter fullscreen mode Exit fullscreen mode

Info

exec runs inside a container that is already running. It does not start a new one. If the container is stopped, start it first. This is the difference between 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:

docker stop web
Enter fullscreen mode Exit fullscreen mode
web
Enter fullscreen mode Exit fullscreen mode

A stopped container does not vanish. It still exists, just not running. docker ps alone will not show it, but docker ps -a will:

docker ps -a
Enter fullscreen mode Exit fullscreen mode
NAMES     STATUS
web       Exited (0)
Enter fullscreen mode Exit fullscreen mode

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:

docker start web
docker ps
Enter fullscreen mode Exit fullscreen mode
NAMES     STATUS         PORTS
web       Up Less than a second   0.0.0.0:8080->80/tcp
Enter fullscreen mode Exit fullscreen mode

Clean 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:

docker rm -f web
Enter fullscreen mode Exit fullscreen mode
web
Enter fullscreen mode Exit fullscreen mode

Now docker ps -a no longer lists web. The container is gone; the nginx image stays cached locally for next time.

Warning

Stopped containers pile up. Every 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:

docker run --rm alpine echo "hello from a throwaway container"
Enter fullscreen mode Exit fullscreen mode
hello from a throwaway container
Enter fullscreen mode Exit fullscreen mode

After 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 run creates and starts a container (add -d, -p, --name, --rm as needed).
  • docker ps / docker ps -a list running / all containers.
  • docker logs reads its output.
  • docker exec -it <name> sh opens a shell inside it.
  • docker stop / docker start pause and resume it.
  • docker rm -f removes 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.

Top comments (0)