Ravindra BagaleCourses & study guides मराठी

Chapter 51: Docker Networking: Port Publishing, Bridge, Host, None, Overlay, Macvlan, DNS and Every Network Command

51.1 Port publishing: reach a container from outside

Friends, 'the container is running but the page does not open in the browser' is the most common classroom question. Today we learn port publishing from the very start: the container's port, the host's port and how they connect.

1. Start from a story

Priya works on a small team at Instagram. Her job today is simple: launch a landing page for a new feature. The page runs in an nginx container on an EC2 server in Mumbai.

She types docker run nginx and the container starts. She opens the browser with the server IP. Nothing. The page just keeps loading and then shows an error.

The container is fine. The problem is the door. A container lives inside its own small private network on the server. Nobody outside can knock on it unless we open a door from the server to the container.

Think of a big apartment building. The building has one gate number on the road, like 8080. Inside, every flat has its own flat number, like 80. The watchman at the gate knows: whoever comes to gate 8080 goes to flat 80. That watchman is port publishing.

In this lesson we learn what a port is, how to open the door with -p, how to choose which network card listens, how to check the mapping, and how to fix the usual mistakes.

User browseropens http://server-ip:8080EC2 security groupmust allow port 8080Host port 8080Docker listens hereContainer port 80nginx listens herenginx answersthe page appears
Picture 1: how a request reaches the container

2. Ports and the first door

Step 1: What is a port?

Why: A server runs many programs. The port number tells the computer which program should get the message.

What: An IP address finds the computer. A port finds the program on that computer. Web servers usually use 80 for HTTP and 443 for HTTPS. A port is a number from 1 to 65535.

Example 1: See who listens on the host

Type this:

sudo ss -tlnp | head -4

You will see:

State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
LISTEN 0      128    0.0.0.0:22          0.0.0.0:*     users:(("sshd",pid=812))

What it means: Only SSH on port 22 is open. Nothing on 8080 yet.

Example 2: Run nginx without a door

Type this:

docker run -d --name nodoor nginx:1.27

You will see:

3f9a1c0d7b2e...

What it means: The container started. Docker printed its long ID.

Example 3: Try to reach it

Type this:

curl -s -m 3 http://localhost:80 || echo failed

You will see:

failed

What it means: Port 80 on the host has nobody listening. The container's port 80 is private.

Step 2: The container's private address

Why: To understand the door, first see the flat number inside the building.

What: Every container on the default bridge gets a private IP like 172.17.0.2. The host can reach it, but the outside world cannot.

Example 4: Find the IP

Type this:

docker inspect nodoor -f '{{.NetworkSettings.IPAddress}}'

You will see:

172.17.0.2

What it means: This IP lives only inside the server.

Example 5: Reach it from the host

Type this:

curl -s http://172.17.0.2 | grep -o '<title>.*</title>'

You will see:

<title>Welcome to nginx!</title>

What it means: It works from the host itself.

Example 6: Clean up

Type this:

docker rm -f nodoor

You will see:

nodoor

What it means: Private IPs change on every run, so we never give them to users.

Step 3: -p HOST:CONTAINER

Why: This is the door. It is the most used flag after -d.

What: The left number is the host port that users hit. The right number is the port inside the container. Docker forwards traffic from left to right.

Example 7: Open 8080 to 80

Type this:

docker run -d --name land -p 8080:80 nginx:1.27

You will see:

a71c4e09d2f3...

What it means: Gate 8080 now leads to flat 80.

Example 8: Test locally

Type this:

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080

You will see:

200

What it means: 200 means OK. The page is served.

Example 9: See it in docker ps

Type this:

docker ps --format '{{.Names}}  {{.Ports}}'

You will see:

land  0.0.0.0:8080->80/tcp, [::]:8080->80/tcp

What it means: Read the arrow: host 8080 goes to container 80, for IPv4 and IPv6.

Step 4: Same container port, many host ports

Why: You may run three copies of the same app on one server.

What: Each container can use port 80 inside, because each has its own network. But host ports must be different, because the host has only one port 8080.

Example 10: Second copy

Type this:

docker run -d --name land2 -p 8081:80 nginx:1.27

You will see:

5be0f1...

What it means: Inside it is still port 80.

Example 11: Clash on purpose

Type this:

docker run -d --name land3 -p 8080:80 nginx:1.27

You will see:

docker: Error response from daemon: ... Bind for 0.0.0.0:8080 failed: port is already allocated.

What it means: Two containers cannot share one host port.

Example 12: Clean the failed one

Type this:

docker rm land3

You will see:

land3

What it means: The container was created but never started, so remove it.

Host port 8080goes to landHost port 8081goes to land2Both containerslisten on 80 insideRulehost ports must be unique
Picture 2: one host, many containers

3. Choose who can reach the door

Step 5: Bind to one IP

Why: Sometimes only the server itself should reach the app, like an admin page or a database.

What: Write the host IP before the ports: -p 127.0.0.1:9090:80. Now only programs on the same server can connect.

Example 13: Localhost only

Type this:

docker run -d --name admin -p 127.0.0.1:9090:80 nginx:1.27

You will see:

9d2e...

What it means: Gate 9090 opens only from inside the building.

Example 14: Check from the host

Type this:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:9090

You will see:

200

What it means: Works locally.

Example 15: Check from your laptop

Type this:

curl -s -m 3 http://13.233.10.20:9090 || echo blocked

You will see:

blocked

What it means: The outside world cannot reach it. Safe.

Step 6: Random host port with -P

Why: In test labs you do not care which port, you only want no clash.

What: Capital -P publishes every port the image declares with EXPOSE, each to a random high port like 32768. docker port shows the choice.

Example 16: Run with -P

Type this:

docker run -d --name rnd -P nginx:1.27

You will see:

c1b2...

What it means: No numbers given.

Example 17: Ask which port

Type this:

docker port rnd

You will see:

80/tcp -> 0.0.0.0:32768
80/tcp -> [::]:32768

What it means: Docker picked 32768.

Example 18: Test it

Type this:

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:32768

You will see:

200

What it means: Random port, same page.

Step 7: Random host port with -p

Why: You want a random host port only for one container port.

What: Leave the host side empty: -p 80. Or give a range: -p 8000-8010:80 and Docker picks a free one from the range.

Example 19: Empty host side

Type this:

docker run -d --name r2 -p 80 nginx:1.27 && docker port r2 80

You will see:

0.0.0.0:32769

What it means: Random again.

Example 20: From a range

Type this:

docker run -d --name r3 -p 8000-8010:80 nginx:1.27 && docker port r3 80

You will see:

0.0.0.0:8000

What it means: First free port in the range.

Example 21: Another from the range

Type this:

docker run -d --name r4 -p 8000-8010:80 nginx:1.27 && docker port r4 80

You will see:

0.0.0.0:8001

What it means: 8000 was taken, so it got 8001.

Step 8: TCP and UDP

Why: Some apps, like DNS or game servers, use UDP instead of TCP.

What: By default -p means TCP. Add /udp to publish UDP. You can publish both for the same port.

Example 22: UDP only

Type this:

docker run -d --name dns -p 5353:53/udp alpine:3.20 sleep 600

You will see:

4e7a...

What it means: Only UDP 5353 is open.

Example 23: Both

Type this:

docker run -d --name both -p 5354:53/tcp -p 5354:53/udp alpine:3.20 sleep 600

You will see:

8b3c...

What it means: Two -p flags, one per protocol.

Example 24: Check

Type this:

docker port both

You will see:

53/tcp -> 0.0.0.0:5354
53/udp -> 0.0.0.0:5354

What it means: Docker lists both.

4. EXPOSE, Compose and what happens underneath

Step 9: EXPOSE in a Dockerfile

Why: Beginners think EXPOSE opens the port. It does not.

What: EXPOSE is only a note for humans and for -P. It says which port the app listens on. Only -p or -P actually opens the door.

Example 25: See the note

Type this:

docker image inspect nginx:1.27 -f '{{json .Config.ExposedPorts}}'

You will see:

{"80/tcp":{}}

What it means: The nginx image says it uses 80.

Example 26: Write your own

Type this:

printf 'FROM nginx:1.27\nEXPOSE 80 8443\n' > Dockerfile && docker build -q -t land:exp .

You will see:

sha256:7f1e...

What it means: Image now notes two ports.

Example 27: Run without -p

Type this:

docker run -d --name exp land:exp && docker port exp

You will see:

(no output)

What it means: EXPOSE alone opened nothing.

Step 10: ports in Compose

Why: Most teams write ports in compose.yaml, not on the command line.

What: The ports list uses the same HOST:CONTAINER text. Always quote it, because YAML can read some numbers strangely.

Example 28: Write the file

Type this:

printf 'services:\n  web:\n    image: nginx:1.27\n    ports:\n      - "8082:80"\n      - "127.0.0.1:9091:80"\n' > compose.yaml

You will see:

(no output)

What it means: Two doors: public 8082, local 9091.

Example 29: Start it

Type this:

docker compose up -d

You will see:

Container demo-web-1  Started

What it means: Same as two -p flags.

Example 30: Check

Type this:

docker compose port web 80

You will see:

0.0.0.0:8082

What it means: Compose shows the first mapping.

Step 11: What Docker does underneath

Why: When something breaks, knowing the machinery helps you debug.

What: Docker adds NAT rules in iptables (or nftables) that send host port traffic to the container IP. A helper called docker-proxy also listens for local traffic.

Example 31: See NAT rules

Type this:

sudo iptables -t nat -L DOCKER -n | grep 8080

You will see:

DNAT  tcp  --  0.0.0.0/0  0.0.0.0/0  tcp dpt:8080 to:172.17.0.3:80

What it means: Traffic to 8080 is rewritten to the container IP.

Example 32: See the proxy

Type this:

ps -ef | grep docker-proxy | grep 8080 | head -1

You will see:

root 2211 ... /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8080 -container-ip 172.17.0.3 -container-port 80

What it means: One proxy process per published port.

Example 33: Host now listens

Type this:

sudo ss -tlnp | grep 8080

You will see:

LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("docker-proxy",pid=2211))

What it means: The gate is really open.

5. Fix the usual problems

Step 12: Works locally, not from the internet

Why: The most common ticket on EC2.

What: If curl on the server gives 200 but your laptop times out, the cloud firewall is blocking. On AWS open the port in the security group, only to the IPs that need it.

Example 34: Symptom

Type this:

curl -s -m 5 http://13.233.10.20:8080 || echo timeout

You will see:

timeout

What it means: Laptop cannot reach.

Example 35: Open the port

Type this:

aws ec2 authorize-security-group-ingress --group-id sg-0abc12345 --protocol tcp --port 8080 --cidr 203.0.113.7/32

You will see:

{"Return": true}

What it means: Only your office IP is allowed.

Example 36: Try again

Type this:

curl -s -o /dev/null -w '%{http_code}\n' http://13.233.10.20:8080

You will see:

200

What it means: Fixed.

Step 13: App listens on 127.0.0.1 inside

Why: Port is published, firewall is open, still connection reset.

What: The app inside must listen on 0.0.0.0. If it listens on 127.0.0.1 inside the container, traffic from Docker never reaches it.

Example 37: Run a wrong app

Type this:

docker run -d --name py -p 8090:8000 python:3.12-slim python -m http.server 8000 --bind 127.0.0.1

You will see:

6a1f...

What it means: The app hides on its own localhost.

Example 38: Test

Type this:

curl -s -m 3 http://localhost:8090 || echo reset

You will see:

reset

What it means: Door opens to an empty room.

Example 39: Fix it

Type this:

docker rm -f py && docker run -d --name py -p 8090:8000 python:3.12-slim python -m http.server 8000 --bind 0.0.0.0 && sleep 1 && curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8090

You will see:

200

What it means: Listening on 0.0.0.0 inside fixes it.

Check yourself

Q1. Which side of -p 8080:80 is the host?

The left side, 8080.

Q2. Does EXPOSE open a port?

No. Only -p or -P opens it.

Q3. How do you make a port reachable only from the server?

-p 127.0.0.1:HOST:CONTAINER.

Q4. Two containers want host port 8080. What happens?

The second fails with port is already allocated.

Q5. curl works on the server but not from a laptop. First check?

The cloud security group or firewall.

Friends, remember: in -p, left is the host, right is the container. Did you understand? Let us try it once more. If the page does not open, check three things: the port in docker ps, whether the app listens on 0.0.0.0, and the security group.

Ravindra Bagale's Tip

Never open a database port on 0.0.0.0. Use 127.0.0.1: or do not publish it at all.

Lab: try at home

  1. Run nginx with -p 8080:80 and open it in a browser.
  2. Run a second nginx on 8081 and try a third on 8080 to see the error.
  3. Publish an admin container on 127.0.0.1:9090 and test from outside.
  4. Run with -P and find the port with docker port.
  5. Write a compose.yaml with two ports and check docker compose port.
  6. Clean up with docker rm -f on every container you made.

Recap

  • A port finds the program; the IP finds the computer.
  • -p HOST:CONTAINER opens the door; host ports must be unique.
  • Add 127.0.0.1: to keep a port private to the server.
  • -P and empty host ports pick random ports.
  • EXPOSE is only a note.
  • When it fails, check docker ps, the app's listen address and the security group.

Ravi Sir's Cards for this lesson

मराठी cards