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:
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:
- Docker looked for an image called
hello-worldon your laptop, and didn't find one. - It pulled (downloaded) the image from Docker Hub, the default registry.
- It created a container from the image and started it.
- 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:
docker run -d -p 8080:80 --name web nginx:1.28
Each part has a job:
| Part | What it does |
|---|---|
docker run | Create a container from an image and start it |
-d | "Detached": run in the background and give you your terminal back |
-p 8080:80 | Send your laptop's port 8080 to port 80 inside the container |
--name web | Call the container web, so you can refer to it easily |
nginx:1.28 | The 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.
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.The everyday commands
See what's running:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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
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
You run docker run nginx:1.28 for the first time. What does Docker do before starting the container?
What does -p 8080:80 mean?
Two containers both listen on port 80 inside. Can they run at the same time?
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?
A container stopped unexpectedly. Which two commands tell you why?
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;
-aadds 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
- Running containers (opens in a new tab) in the official Docker docs covers every option of
docker run. - Publishing and exposing ports (opens in a new tab) explains port forwarding in more depth.