There are two things called Docker and it helps to know which one you are installing.
Desktop or Engine
Docker Desktop is what you want on a Mac or a Windows machine. Neither has a
Linux kernel, and containers need one — so Desktop quietly runs a small Linux VM and puts the
docker command on your machine talking to it. It also brings a GUI, Compose, and
buildx. It is free for personal use and for small companies; large ones need a licence.
Docker Engine is the daemon on its own, for Linux, where no VM is needed because the kernel is already there. This is what runs on servers.
Everything in this track works on either. Check what you have:
docker version
docker infoIf docker version prints a Client block and then an error about the daemon, Docker
is installed but not running — start Docker Desktop, or sudo systemctl start docker.
Run something
docker run hello-worldFour things happened. Docker looked for the hello-world image locally and did not
find it; it pulled it from Docker Hub; it created a container from it; it ran the container's
command, which printed a paragraph and exited.
Now something that stays up:
docker run -d -p 8080:80 --name web nginx:1.27-alpineVisit http://localhost:8080 and nginx answers. Every part of that line is doing
work:
-d — detached. Without it your terminal is attached to the container's output and
Ctrl-C stops the container. Useful when you want the logs; annoying when you want your prompt.
-p 8080:80 — publish. Host port on the left, container port on the
right, and mixing them up is the most common Docker mistake there is. Lesson 10 is about
nothing else.
--name web — a name you choose. Without it Docker invents one like
nostalgic_hopper and you have to look it up every time.
nginx:1.27-alpine — the image, and after the colon the tag. Leave the tag off and
you get :latest, which is not a promise of anything (lesson 18).
The six commands you will actually use
docker ps # what is running
docker ps -a # ... including what has stopped
docker logs -f web # follow its output
docker exec -it web sh # a shell INSIDE the running container
docker stop web # SIGTERM, then SIGKILL after 10s
docker rm web # delete the stopped containerexec is the one worth getting comfortable with early. It starts a
second process in an existing container, so you can look around:
$ docker exec -it web sh
/ # ls /usr/share/nginx/html
50x.html index.html
/ # exit-it is two flags: -i keeps stdin open, -t gives you a
terminal. Without both, a shell starts and immediately exits with no explanation.
Note also sh, not bash. Alpine-based images have no
bash, and docker exec -it web bash fails with
executable file not found — which reads like the container is broken rather than
minimal.
--rm, and why your disk fills up
A stopped container is not gone. It sits there holding its writable layer until you remove it, which is occasionally what you want and usually just clutter:
docker run --rm -p 8080:80 nginx:1.27-alpine--rm deletes the container when it exits. Use it for anything throwaway.
When the clutter has already accumulated:
docker ps -a # look first
docker container prune # remove all STOPPED containers
docker image prune # remove dangling (untagged) images
docker system df # what is actually using the spaceLook before you prune. docker system prune -a is the one people
paste from a forum answer; it also removes every image not currently used by a container, which
means re-pulling and rebuilding everything. And --volumes on top of that deletes your
databases.
"It exited immediately"
The single most common first problem. Try it deliberately:
$ docker run -d --name probe alpine:3.21
$ docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
$ docker ps -a
CONTAINER ID IMAGE COMMAND STATUS NAMES
c3f1a2b9d4e5 alpine:3.21 "/bin/sh" Exited (0) 2 seconds ago probeNothing is broken. A container lives exactly as long as its main process, and
that image's main process is /bin/sh — which, given no input and no terminal, reaches
end-of-file and exits successfully. Exit code 0 means it did what it was told.
The rule this comes down to: the process must stay in the foreground. A
container whose command starts a daemon and returns exits the instant the command returns, no
matter how healthy the daemon it launched was. It is why the nginx images run
nginx -g 'daemon off;', and why lesson 4 cares about how CMD is
written.
Clean up what we started:
docker stop web
docker rm web probe