Long-running containers
hello-world quits instantly, but real workloads are servers that run forever. Starting one properly looks like this:
docker run -d --name web nginx
| Piece | Effect |
|---|---|
-d | detached mode, runs in the background and returns the prompt |
--name web | a human name instead of an invented one |
nginx | a tiny and extremely common web server image |
Without -d, the terminal is glued to the container's output. Without --name, Docker invents something like jolly_wozniak, and every later command such as docker logs web or docker stop web is easier with a name you chose.
The container is now serving a web page, and opening http://localhost in a browser still shows nothing. The reason is that a container's ports are isolated from the host until they are explicitly published, which is what the next section fixes.
Publishing ports
A port is a number from 0 to 65535 that the operating system uses to route incoming network traffic. Every server program claims a port when it starts, and the OS delivers each arriving connection to whichever program claimed the port it was addressed to. That rule is how one machine can run a web server, a database, and ssh at once without their traffic mixing. (A numbered door on a big building is a fine mental picture, but the mechanism is just that routing rule.)
nginx claims port 80, but that is port 80 inside the container's isolated network, which your browser cannot reach.
The -p flag fixes that by forwarding: traffic arriving at a chosen port on your machine is relayed to a port inside the container:
docker run -d --name web -p 8080:80 nginx
Read -p 8080:80 as host:container, left is your machine, right is inside. Traffic hitting localhost:8080 on your laptop is forwarded to port 80 in the container. Now http://localhost:8080 shows the nginx welcome page.
The order trips everyone up at first. Left side: the port outside traffic uses on your machine. Right side: the port the app inside is listening on.
Two containers, one image, one port conflict
Ports explain the first error every Docker user hits. Start two nginx containers:
docker run -d --name web1 -p 8080:80 nginx # works docker run -d --name web2 -p 8080:80 nginx # fails
The second fails with bind: address already in use. The operating system allows exactly one program to claim a given host port, and host 8080 is already taken by the forwarding for web1. The fix is a different host port:
docker run -d --name web2 -p 8081:80 nginx # works
Note what did not change: both containers still listen on 80 inside, because each container has its own isolated network. Container ports can repeat across containers. Host ports cannot.
Reading a published port mapping
With docker run -d -p 9000:3000 myapp and an app listening on port 3000 inside, the browser reaches it at http://localhost:9000.
-p takes host first and container second. 9000 is the port on your machine, 3000 is the port inside the container.
| Side of the colon | Belongs to |
|---|---|
| left, 9000 | the host |
| right, 3000 | the container |
The browser lives on the host, so it connects to 9000 and Docker forwards to 3000. Visiting localhost:3000 reaches nothing at all, because the container's ports stay isolated until published.
Fixing a host port collision
When a second container with -p 8080:80 fails with bind: address already in use, the fix is -p 8081:80 on the second one. Host ports must be unique while container ports can repeat.
The conflict is entirely on the host side, because only one program can claim host port 8080 and the first container's forwarding already did.
| Container | Host port | Container port | Allowed |
|---|---|---|---|
| first | 8080 | 80 | yes |
| second | 8080 | 80 | no, collision |
| second | 8081 | 80 | yes |
Each container has its own network, so both listening on container port 80 is completely fine. Any free host port works for the second container.
The flag for host 5000 to container 3000
The flag is -p 5000:3000.
The left number is the host port that browsers and other machines use, and the right number is the port the app listens on inside the container. --publish is the long form of the same flag.
| Form | Meaning |
|---|---|
-p 5000:3000 | host 5000 forwards to container 3000 |
--publish 5000:3000 | identical |
The order is worth committing to memory, since reversing it produces a mapping that either fails or silently forwards the wrong way. Host always goes on the left of the colon.