The last article covered what a container actually is. This one is about actually running one. By the end, you will have installed a real container engine and used it to pull, run, inspect, stop, and remove a container—the handful of commands that cover most of day-to-day container work.
Installing a container engine
You need an engine before you can run anything. On Mac and Windows, that is almost always Docker Desktop: it installs the Docker CLI, a background engine, and a small Linux VM to host containers, since neither macOS nor Windows runs Linux containers natively. On Linux, you install Docker Engine directly—no VM required, since you are already running a Linux kernel.
Install whichever matches your platform from Docker's own documentation, then confirm it worked:
docker --version docker info
docker --version confirms the CLI is installed. docker info confirms the engine itself is actually running and reachable—a common early snag is having the CLI installed but the engine (or Docker Desktop) not started.
This series will cover Podman, a second engine, in a later part. Everything below works the same way there, with one command renamed. For now, install Docker and get comfortable with it—switching engines later is a small change, not a restart.
Running your first container
The traditional first command is almost a rite of passage:
docker run hello-world
Watch the output closely. It explains its own mechanism: Docker looked for the hello-world image locally, did not find it, pulled it from Docker Hub, created a container from it, ran it, and printed a message. The container then exited, because all it does is print that message and stop.
That sequence—pull if missing, create, start—is what docker run always does. Try something more useful: an interactive shell inside a real Linux distribution.
docker run -it ubuntu bash
-it combines two flags: -i keeps input open, -t allocates a terminal. Without both, you would not get an interactive shell. You are now inside a container, running commands against its own filesystem, isolated from your host. Type exit to leave.
The core lifecycle commands
Five commands cover most of what you need day to day.
docker ps # running containers docker ps -a # all containers, including stopped ones docker stop <container> # stop a running container docker start <container> # start a stopped container docker rm <container> # remove a stopped container
The distinction between docker ps and docker ps -a trips up almost everyone at first. A container that exits—like hello-world did—does not disappear. It stops, and stopped containers only show up with -a. If you are looking for something you know you ran and cannot find it, that is almost always where it went.
docker logs <container> shows a container's output after the fact, which matters once you start running things in the background instead of watching them interactively. docker exec -it <container> sh opens a shell inside an already running container—different from docker run, which always creates a new one.
Running something you would actually use
hello-world proves the mechanism works. A web server is closer to real usage:
docker run -d -p 8080:80 --name my-nginx nginx
Three new pieces:
-druns the container detached, in the background, instead of attaching your terminal to it.-p 8080:80publishes the container's port 80 to your machine's port 8080. Without it, the container's network is isolated and nothing outside can reach it.--name my-nginxgives the container a name you choose, so later commands can referencemy-nginxinstead of a generated ID.
Visit http://localhost:8080 and you will see nginx's default page, served from inside the container. Check docker logs my-nginx to see its access logs, then docker stop my-nginx when you are done.
Cleaning up after yourself
Containers and images accumulate quietly. A stopped container still exists until you remove it; a pulled image stays cached even after every container using it is gone.
docker rm my-nginx # remove a stopped container docker rm -f my-nginx # stop and remove in one step docker images # list locally cached images docker rmi <image> # remove a specific image
A full cleanup workflow—including docker system prune and the habits that keep a machine from filling up with unused images—gets its own treatment later in this series. For now, it is enough to know that docker ps -a and docker images are where to look when you are wondering what is actually sitting on your machine.
Common mistakes
- Forgetting
-itfor an interactive shell. Without it, the container starts and exits immediately, or your keystrokes go nowhere. - Running a server without
-pand wondering why it is unreachable. A container's network is isolated by default; nothing is exposed until you publish a port. - Losing track of stopped containers.
docker psalone will not show them—docker ps -awill. - Reaching for
docker runto get back into a container you already started. That creates a new one. Usedocker execon the existing container instead, ordocker startif it is stopped.
Practical checklist
-
docker --versionanddocker infoboth succeed. - I have run a container interactively with
docker run -it. - I can find a stopped container with
docker ps -a. - I have run a container with a published port and reached it from a browser.
- I know the difference between
docker run(create new) anddocker exec(enter existing). - I have removed a container and an image I no longer need.
Next in the series: Building Your Own Image: The Dockerfile Basics. We will stop pulling other people's images and start building our own.


