Skip to content

Docker hello-world: Run Your First Container

With Docker installed (see Install Docker Desktop on Windows 11), it's time to run a container. The official hello-world image is the smallest possible test: it downloads in a second, prints a message and stops. But behind that one command Docker does a lot of work, and understanding it now makes every later command easier.

In this lab you'll follow the image download and the container execution step by step. The outputs come from a real run on Docker Engine 29.

Run the command

docker run hello-world
Output (first run)
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
4f55086f7dd0: Pulling fs layer
4f55086f7dd0: Download complete
4f55086f7dd0: Pull complete
d5e71e642bf5: Download complete
Digest: sha256:c567b231f65614688711a75f31518a064258a51d442117a3e771a3a8fdba78ef
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

The output has two parts. The first eight lines come from Docker (downloading the image). Everything from "Hello from Docker!" comes from the program inside the container.

Part 1: the image download, line by line

LineWhat happened
Unable to find image 'hello-world:latest' locallyThe daemon looked in its local image store first. You wrote hello-world without a tag, so Docker used the default tag latest. It wasn't there yet.
latest: Pulling from library/hello-worldThe daemon contacted the default registry, Docker Hub. library/ is the namespace for Docker's official images, so the full name is docker.io/library/hello-world:latest.
4f55086f7dd0: Pulling fs layer ... Pull completeThe image is made of layers. Each one is downloaded, checked and unpacked. hello-world has only one filesystem layer.
Digest: sha256:c567b2...A unique fingerprint of exactly this image version. The same digest always means exactly the same content.
Status: Downloaded newer imageThe image is now stored locally and won't be downloaded again.

The image is now in your local list:

docker images
Output
IMAGE                ID             DISK USAGE   CONTENT SIZE   EXTRA
hello-world:latest   c567b231f656       25.9kB         9.49kB   U

It's tiny: about 26 KB on disk. The U in the EXTRA column means the image is in use by at least one container (even a stopped one).

Part 2: the container execution

The message printed by the container describes the four steps itself, and they map exactly onto the Docker architecture:

  1. Client to daemon: docker.exe sent a "create and start a container" request to the Docker daemon through its REST API.
  2. Daemon to registry: the image wasn't local, so the daemon pulled it from Docker Hub (part 1 above).
  3. Daemon creates and starts a container: it added a thin writable layer on top of the read-only image, set up isolation, and started the image's default command. For hello-world that command is a single small program called /hello.
  4. Output streamed back: the program wrote its text to standard output; the daemon streamed it to your client, which printed it in your terminal.

You can see the default command and size with docker image inspect:

docker image inspect hello-world --format "Os={{.Os}} Arch={{.Architecture}} Cmd={{.Config.Cmd}} Size={{.Size}}"
Output
Os=linux Arch=amd64 Cmd=[/hello] Size=20399

The whole image is one 20 KB program and nothing else: no shell, no operating system tools. That's enough because a container doesn't need a full OS; it uses the host's kernel.

Why the container stops immediately

Right after the message, the container is no longer running:

docker ps
Output
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

The list is empty. A container lives only as long as its main process. /hello printed its text and exited, so the container stopped. It wasn't deleted, though. Add -a to see stopped containers too:

docker ps -a
Output (after running hello-world twice)
CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES
6a35917c5cd0   hello-world   "/hello"   10 minutes ago   Exited (0) 10 minutes ago             nifty_ellis
79313307bc7e   hello-world   "/hello"   10 minutes ago   Exited (0) 10 minutes ago             eloquent_meninsky
  • Exited (0): the program ended with exit code 0, which means success.
  • Each docker run creates a new container from the same image, so two runs left two containers.
  • We didn't use --name, so Docker generated random names such as nifty_ellis.

Run it a second time: no download

docker run hello-world

This time the output starts straight with "Hello from Docker!". There are no "Unable to find image" or "Pulling" lines, because the image is already stored locally. Only the first run of an image pays the download cost; containers start from the local copy afterwards.

Clean up automatically with --rm

To avoid collecting stopped containers, add --rm. Docker removes the container as soon as it exits:

docker run --rm hello-world

After this run, docker ps -a still showed only the same two containers: the --rm container was removed automatically. To remove the old ones and the image itself:

docker container prune          # removes ALL stopped containers (asks for confirmation)
docker rm nifty_ellis eloquent_meninsky   # or remove specific ones
docker rmi hello-world          # remove the image (no container may use it)

Be careful with prune commands on a machine where you have other work: docker container prune removes every stopped container, not only the ones from this lab. Removing containers by name is safer.

What you learned

ConceptSeen in this lab
Image lookup orderLocal image store first, then the registry (Docker Hub)
Default taghello-world means hello-world:latest
Layers and digestImages download in layers and are identified by a sha256 digest
docker run = pull (if needed) + create + startThe four steps in the hello-world message
Container lifetimeIt stops when its main process exits
Stopped containers remaindocker ps -a; remove them with docker rm or --rm

Interview questions

What does docker run do when the image isn't available locally?

It pulls the image from the configured registry (Docker Hub by default), then creates a container from it and starts it. If the image is local, no download happens.

Why does the hello-world container stop right after it runs?

A container runs only as long as its main process. The /hello program prints a message and exits with code 0, so the container moves to the Exited state.

What is the difference between docker ps and docker ps -a?

docker ps lists only running containers. docker ps -a lists all containers, including stopped (exited) ones.

Summary

  • docker run hello-world checks the local store, pulls from Docker Hub if needed, creates a container and starts it.
  • Pull output shows the image's layers and its sha256 digest.
  • The container exits as soon as its program finishes; it stays in docker ps -a until removed.
  • Second runs reuse the local image; --rm deletes the container automatically when it exits.

Next lab: Run an Nginx container on port 8080, a container that keeps running and serves a website.