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:

  • -d runs the container detached, in the background, instead of attaching your terminal to it.
  • -p 8080:80 publishes 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-nginx gives the container a name you choose, so later commands can reference my-nginx instead 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 -it for an interactive shell. Without it, the container starts and exits immediately, or your keystrokes go nowhere.
  • Running a server without -p and 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 ps alone will not show them—docker ps -a will.
  • Reaching for docker run to get back into a container you already started. That creates a new one. Use docker exec on the existing container instead, or docker start if it is stopped.

Practical checklist

  • docker --version and docker info both 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) and docker 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.