CoursesDocker for beginnersInstalling Docker & your first run

Installing Docker & your first run

hello-world, and what just happened.

Beginner10 min · lesson 4 of 16
In plain terms
The docker CLI talks to the Docker daemon. The daemon pulls images, creates containers, and manages networks and volumes.

Installing Docker puts two programs on the machine. The Docker daemon (dockerd) is a background service: it pulls images, starts containers, and manages networks and volumes. You do not talk to it with a GUI on Linux Engine. The docker CLI is the client: you type a command, it sends an API request to the daemon. Almost every lesson in this course is that client/daemon split.

Which pieces you install depends on the machine in front of you. On Linux you install Docker Engine, and it runs containers directly. A container is an ordinary program running on your machine, fenced off in its own little space so it behaves as if it had the whole computer to itself. Linux already has the kernel a container needs. (The kernel is the core of the operating system, the part that actually talks to the hardware.) On a Mac or on Windows you install Docker Desktop instead. Containers need a Linux kernel, and your Mac or Windows machine doesn't have one, so Desktop quietly runs a small Linux virtual machine in the background, a whole extra computer simulated in software, and keeps your containers inside it. Either way you type the same docker commands. The examples below are Linux, because the one error nearly every beginner hits lives there.

Check that both halves are talking

Before you run anything real, get the two halves to say hello. The command docker version asks each of them to report in, and it prints one block per answer.

terminal
$ docker version
output
Client: Docker Engine - Community
Version: 27.3.1
API version: 1.47
Go version: go1.22.7
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 27.3.1
API version: 1.47 (minimum version 1.24)
Go version: go1.22.7
OS/Arch: linux/amd64

Read it from the top. Client is the CLI you typed. Server is the daemon. Two blocks with version numbers means the client reached the daemon. If Server is missing or errors, the daemon is not running, or your account cannot talk to it. On Linux: sudo systemctl start docker. On Docker Desktop: open the app and wait until it is idle. Permission on a fresh Linux install is the usual next failure — trigger it on purpose below.

Your first container

The usual first run is the hello-world image. An image is a read-only bundle of files plus the default process to start. A container is a running (or exited) instance of that image. hello-world lives on Docker Hub. Its default name is hello-world:latest; the part after the colon is the tag, and latest is what Docker fills in when you omit one.

Type it and read what comes back.

terminal
$ docker run hello-world
output
docker: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head "http://%2Fvar%2Frun%2Fdocker.sock/_ping": dial unix /var/run/docker.sock: connect: permission denied.
Run 'docker run --help' for more information

Nothing is broken here, so do not reinstall. Read the error: permission denied on /var/run/docker.sock. That socket is how the client reaches the daemon. On Linux it is owned such that only root (or members of the docker group) may use it. Your client was refused. docker version on the same machine shows the same Server error for the same reason.

The fix is to let your account use that hatch. Docker creates a group named docker for exactly this purpose, and anyone in that group is allowed to reach the daemon. You add yourself once, with usermod. The -aG flags mean append the account to the named group and leave the groups it already belongs to alone. One snag: your terminal session started before you joined the group, so it hasn't noticed the change. Running newgrp docker opens a fresh session that carries the new membership, and logging out and back in does the same thing. Then the command that failed goes through.

terminal
$ sudo usermod -aG docker $USER
$ newgrp docker
$ docker run hello-world
output
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:d000bc569937abbe195e20322a0bde6b2922d805332fd6d8a68b19f524b7d21d
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.

That wall of text is Docker narrating its own steps. It looked for hello-world on your machine, didn't find it, and said so on the Unable to find image line. Then it pulled a copy down from Docker Hub. Then it created a container from that image and started the one small program inside, which printed the greeting. The program had nothing left to do, so it exited, and the moment a container's program exits, the container stops. Every docker run you ever type follows the same path: look locally, pull the image if it's missing, create the container, start it, and let it run until its program ends.

What every docker run does
1look locally
is the image on this machine?
2pull from Docker Hub
download it if it is missing
3create a container
set up from the image, not started yet
4run its program
prints the hello message
5program exits
the container stops
hello-world runs one command and quits, so its container stops the moment it finishes. A web server stays up instead, because its program never ends on its own.
The docker group is basically root
Here's the part most tutorials skip past. Anyone in the docker group can start a container that mounts your entire hard drive and rewrite it as the root user. Joining that group quietly hands out root-level power, even to an account with no admin rights at all. On your own laptop that's a fair trade for the convenience. On a shared machine or a real server, put people in the docker group deliberately, because you decided they need it, not because a tutorial told you to run the command. If that trade bothers you, Docker has shipped a rootless mode since version 20.10 that runs the daemon and your containers as your own user instead of root, which takes the group question off the table. It asks for a little setup in return, including extra work before a container can use a port below 1024.

The daemon has a name worth knowing: dockerd. That is the program doing the real work, pulling images, creating containers, wiring up networks, and on Linux it is what sits on the other side of /var/run/docker.sock. On Docker Desktop it lives inside that small Linux virtual machine, so your order pad is reaching across to another computer to hand over the ticket. Most install trouble is a break somewhere in that handoff, and the error text names which side stopped you, the way the permission line above did.

So make your smoke test boring, and run it in this order. docker version should print a Client block and a Server block. docker run --rm hello-world should print the greeting. docker ps should return a table, even an empty one with nothing but column headings. Once all three behave, you can start blaming your own app. Skip the check and you can lose an afternoon debugging an nginx port mapping on a machine where the daemon never started.

One habit to skip while you are here. You can put sudo in front of every docker command and it will work, which is how plenty of people end up typing sudo docker for years. That habit hides the group setup instead of fixing it, and it stops helping the day you sit at a machine where your account has no sudo rights.

Try this

Prove both halves are alive, then run hello-world with --rm so the finished container doesn't linger on your machine.

terminal
docker version --format "{{.Server.Version}}"
docker info --format "{{.Driver}} {{.OperatingSystem}}"
docker run --rm hello-world
output
27.x.x
overlay2 Docker Desktop
Hello from Docker!
This message shows that your installation appears to be working correctly.

Takeaway

The docker command is a client and nothing else. If docker version prints no Server block, or hello-world can't reach the socket, that's the engine talking, not your app. Get the engine answering first, before you touch a container of your own.

Quick check
01A brand new Linux box answers your very first docker run hello-world with a line that names /var/run/docker.sock and finishes with permission denied. Which conclusion does that one line actually support?
Incorrect — No download was ever attempted. Fetching an image is work the background service does, and your request never reached it.
Incorrect — A broken setup does not produce a complaint this precise. The file it names exists and answered you; it simply would not let you in.
Correct — Being refused proves somebody was there to refuse you. The work happens under an admin-owned file, and your login is not yet on the approved list.
Incorrect — When nothing is listening you get a missing-file style complaint instead. A refusal means something was awake and chose to say no.
02You type sudo usermod -aG docker $USER on that same Linux box, then immediately retry hello-world in the window you are already sitting in and get the exact same refusal. Why?
Incorrect — Dropping the a would wipe every other group your login belongs to, and the window you are typing in still would not notice the change.
Correct — Pick up a session that reads that list again, either by running newgrp docker or by ending your login and starting another, and the same command sails through.
Incorrect — Nothing on the engine side is out of date. The stale piece is the shell in front of you, which is why a fresh login alone is enough.
Incorrect — That command is for an engine that is not running, and yours clearly is. Starting it again says nothing about which groups your current window thinks you have.
03A colleague on a shared build server has no sudo rights at all, so you put their login in the docker group to unblock their test runs. What have you quietly handed them?
Incorrect — There are no lanes inside that group. Whoever can reach the engine can ask it about anything it is managing, no matter who started it.
Incorrect — The connection does not come in halves. Once they can talk to the engine at all, they can ask it to create a container on any terms they name.
Incorrect — No trimming happens anywhere. The engine on the far side already runs with full admin rights and carries out whatever it is asked to do.
Correct — That is the real trade this group makes. Fine on a laptop you own, and worth a deliberate decision on a machine other people depend on.

Related