Docker basics · Lesson 2 of 8 · about 25 minutes

Your first container

Run a web server in a container, reach it from your browser, look inside it, and learn the handful of commands you'll use every day.

By the end you'll know

  • What actually happens when you type docker run
  • How to reach a container from your browser, with ports
  • How to list, read the logs of, look inside, stop and remove containers
  • What happens to changes made inside a container

Hello, container

In Lesson 1 you checked that Docker is running. Now run your first container:

BASH
docker run hello-world

You'll see a few lines about pulling, then Hello from Docker! and a short explanation. A lot just happened in a few seconds:

  1. Docker looked for an image called hello-world on your laptop, and didn't find one.
  2. It pulled (downloaded) the image from Docker Hub, the default registry.
  3. It created a container from the image and started it.
  4. The program inside printed its message and finished, so the container stopped.

Run the same command again. This time there's no pulling: the image is already on your laptop, so the container starts straight away. Images are downloaded once and reused.

Run a web server

hello-world prints and stops. Most real containers keep running, like a web server. Start nginx, a popular one:

BASH
docker run -d -p 8080:80 --name web nginx:1.28

Each part has a job:

PartWhat it does
docker runCreate a container from an image and start it
-d"Detached": run in the background and give you your terminal back
-p 8080:80Send your laptop's port 8080 to port 80 inside the container
--name webCall the container web, so you can refer to it easily
nginx:1.28The image to use: nginx, version 1.28

Now open http://localhost:8080 in your browser. You'll see "Welcome to nginx!", served from inside a container.

Ports: how traffic gets in

A container has its own network, separate from your laptop's. nginx listens on port 80 inside the container, but nothing outside can reach it until you say so. -p 8080:80 does that: anything that arrives at port 8080 on your laptop is passed to port 80 in the container. The order is always your laptop's port first, the container's second.

Think of an office phone system. The company has one main number; each person inside has a short extension. "Dial 8080 and you'll be put through to extension 80 in the web office" is what -p 8080:80 sets up. Two offices can both have an extension 80, because each office has its own; but the main numbers on the outside must be different.
How -p 8080:80 worksYour browser asks for localhost port 8080. Docker forwards that to port 80 inside the container called web, where nginx is listening. A second container, api, also listens on its own port 80, and is reached from your laptop's port 8081. Each container has its own ports, so both can use 80 inside; the ports on your laptop must be different.Your laptopBrowserlocalhost:8080Dockerforwards your laptop's ports8080 → web:808081 → api:80Container: webnginx listening on port 80Container: apialso listening on its own port 80Your laptop's port first, the container's second. Inside, every container has its own ports.
Docker forwards a port on your laptop to a port inside a container. Containers can reuse the same port inside, because each has its own network; the ports on your laptop must be unique.

The everyday commands

See what's running:

BASH
docker ps

One line per running container: its ID, image, how long it's been up, its ports (0.0.0.0:8080->80/tcp) and its name. Add -a (docker ps -a) to also see stopped containers. Your hello-world containers are there, with the status Exited (0).

Read its logs. Whatever the program inside prints ends up here. Reload the page in your browser a couple of times, then:

BASH
docker logs web

Each reload added a line: nginx logging the request. Add -f (docker logs -f web) to keep watching as new lines arrive, and press Ctrl+C to stop watching.

Look inside it. docker exec runs a command in a container that's already running:

BASH
docker exec web cat /etc/os-release

It prints the name of the Linux system inside the container. Your laptop might be a Mac, but the container has its own small Linux files. Add -it and a shell to look around yourself:

BASH
docker exec -it web sh

Your prompt changes: you're inside. Try ls /usr/share/nginx/html, where nginx keeps its pages, then type exit to come back out.

Changes inside a container

Change the page nginx serves, from inside the container:

BASH
docker exec web sh -c 'echo "<h1>Changed inside the container</h1>" > /usr/share/nginx/html/index.html'

Reload your browser: the page has changed. Now stop the container, and start it again:

BASH
docker stop web
docker start web

Reload. Still the changed page. Stopping a container is like turning a computer off: what's on its disk stays. Now remove it, and run a fresh one from the same image:

BASH
docker rm -f web
docker run -d -p 8080:80 --name web nginx:1.28

Reload. "Welcome to nginx!" is back. docker rm deleted the container and everything changed inside it, and the new container started from the image, which never changed. That's the cookie cutter from Lesson 1: a container is a copy you can change or throw away, and the image stays the same.

Two lessons from that:

  • Don't make changes inside a running container that you want to keep. They live only as long as that container. Lesson 4 shows how to put your changes into an image instead, and Lesson 5 how to keep data outside the container.
  • Removing a container is normal. You'll create and remove them all the time.

-f in docker rm -f stops the container first if it's still running. Without it, Docker refuses to remove a running container.

One-off containers

Not every container is a server. Sometimes you want to run one command, or poke around, and throw the container away afterward. --rm removes the container as soon as it stops:

BASH
docker run --rm -it busybox:1.36 sh

You're in a shell inside a tiny Linux image called BusyBox. Type exit, then docker ps -a: no leftover container. Without --rm, every one-off run leaves a stopped container behind, and they pile up.

In my real setup

I use one-off containers to look for city officials' web pages for CivicGrid. The search script needs Python, several libraries and a whole headless Chrome browser to load pages that need JavaScript. None of that is installed on my home server. It all lives in one image, and each run is a docker run --rm that starts, does its job, and disappears, leaving nothing behind.

My CivicGrid API's instructions start the same way: docker run --rm -p 8000:8000 ... runs the API on my laptop, reachable at port 8000, exactly like nginx here.

Break it on purpose

Use a name that's taken. web is still running. Try to start a second container with the same name:

BASH
docker run -d -p 8081:80 --name web nginx:1.28

Conflict. The container name "/web" is already in use. Names must be unique, including the names of stopped containers. Remove the old one, or pick another name.

Use a port that's taken. Now use a new name, but the same laptop port:

BASH
docker run -d -p 8080:80 --name web2 nginx:1.28

An error saying port 8080 is already allocated (the exact wording varies a little between systems). Only one thing at a time can use a port on your laptop. Change the first number, -p 8081:80, and it works. The second number can stay 80, because inside its own container, port 80 is free. If docker ps -a shows a web2 left behind by the failed attempt, remove it with docker rm web2.

Watch a container fail. Run a container whose program fails on purpose:

BASH
docker run --name oops busybox:1.36 sh -c 'echo "something went wrong"; exit 3'
docker ps -a

oops shows Exited (3). That number is the exit code: 0 means the program finished successfully, and anything else means it failed. The logs of a stopped container are still there:

BASH
docker logs oops

When a container stops when it shouldn't, these are your first two moves: docker ps -a for the exit code, and docker logs for what the program said before it stopped.

Clean up

BASH
docker rm -f web web2 oops

The images stay on your laptop, ready for next time. Lesson 3 covers images, and how to clean those up too.

Check yourself

  1. You run docker run nginx:1.28 for the first time. What does Docker do before starting the container?

  2. What does -p 8080:80 mean?

  3. Two containers both listen on port 80 inside. Can they run at the same time?

  4. You change a file inside a running container, then remove the container and run a new one from the same image. Is the change there?

  5. A container stopped unexpectedly. Which two commands tell you why?

  6. What does --rm do in docker run --rm -it busybox:1.36 sh?

Key terms

  • Pull: download an image from a registry.
  • Port publishing (-p): forwarding a port on your laptop to a port inside a container. Laptop first, container second.
  • Detached (-d): running a container in the background.
  • docker ps: lists running containers; -a adds stopped ones.
  • docker logs: shows what the program in a container printed, even after it stopped.
  • docker exec: runs a command inside a running container.
  • Exit code: the number a program ends with. 0 means success; anything else means it failed.
  • --rm: removes the container automatically when it stops.

Go deeper