[Docker Series Part 8] Mastering Ports, Networks, and Service Names

한국어 버전

Part 7 separated dev and prod containers. Now we map how those containers actually communicate. The mission is simple: build a clear picture of host ports, container ports, and service names. The example stack uses three services: nginx web, Node API, and PostgreSQL DB.

How this post flows

  1. Draw a mental diagram that separates host, container, and service names
  2. Read a web + API + DB Compose snippet for ports and networks
  3. Understand how service-name DNS works inside Compose
  4. Mini Lab: confirm each path with success and failure cases
  5. Summarize the addresses people mix up most often

Reading card

  • Estimated time: 17 minutes
  • Prereqs: docker compose up and basic port concepts
  • After reading: you can explain port mappings and service names to a beginner.

Mental diagram

Keep the following text diagram in mind and confusion disappears.

[Browser] --localhost:8080--> [Host OS] --port mapping--> [web container 80]
                                               |
                                               +--> other containers on the same network (service name)
  • The browser always connects with localhost + the published port.
  • Port mappings follow the ports: "8080:80" pattern (host:container).
  • Containers on the same network resolve service names (api, db, ...) to each other through internal DNS.

Separating those areas keeps 8080 vs. 80 or api:4000 vs. localhost:4000 from blurring together.

Before reading the Compose file, keep one lookup rule nearby: use localhost from the host, and use service names from one container to another.

Your location Target Address
Browser or host terminal web http://localhost:8080
Host terminal API http://localhost:4000
web container API http://api:4000
api container DB db:5432

Web + API + DB Compose snippet

Here is the same diagram written as Compose. Watch how the ports and shared network encode those paths.

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./public:/usr/share/nginx/html:ro
    depends_on:
      - api
    networks:
      - app-net

  api:
    build: ./api
    environment:
      - DATABASE_URL=postgres://postgres:your-db-password@db:5432/app
    ports:
      - "4000:4000"
    networks:
      - app-net

  db:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=your-db-password
      - POSTGRES_DB=app
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-net

volumes:
  db-data:

networks:
  app-net:
    driver: bridge
  • web exposes host 8080 → container 80 and serves static files read-only.
  • api keeps 4000:4000 open so you can debug it from the host.
  • db exposes no host port at all, so only containers on the same network can reach db:5432.
  • DATABASE_URL contains db:5432. Here db is the service name, and Compose's internal DNS resolves it on the shared network.
  • depends_on starts api before web, but it does not guarantee that the API is ready to answer requests yet. Part 9 covers that gap with health checks.

In a real project, make sure the API server listens on 0.0.0.0 inside the container. If it listens only on 127.0.0.1 (localhost), requests arriving through the container network interface will miss it.

Service names act as internal DNS

That db value in DATABASE_URL is the important clue. Compose provides internal DNS on the shared project network, so the service name becomes the address other containers use.

Compose creates a DNS record for each service name on every shared network. Within one network you can reach another container with service-name:port.

Goal Address
View the web app from the browser http://localhost:8080
Call the API from the host http://localhost:4000
Web container calling the API http://api:4000
API container connecting to the DB postgres://...@db:5432/app

localhost always points to the machine or container you are currently inside. Containers use service names to find each other. Confusing those two namespaces creates most networking bugs.

Therefore: use localhost on the host, use service names inside the Compose network. The host cannot normally resolve api, because that DNS name exists only inside the Compose network.

Mini Lab: trace every hop

  1. Save the Compose file and run docker compose up -d.
  2. curl localhost:8080 to confirm the static site loads.
  3. curl localhost:4000/health to confirm the API responds.
  4. Intentionally try the wrong address from inside web: docker compose exec web sh -c "wget -qO- http://localhost:4000/health".
  5. That request should fail, because localhost inside web points back to the web container itself.
  6. Try the correct address instead: docker compose exec web sh -c "wget -qO- http://api:4000/health".
  7. Test DB readiness from inside db with docker compose exec db pg_isready -U postgres -d app.
  8. Run docker compose down to clean up.

Repeating those steps makes it easy to narrate “I am connecting from X to Y through Z.”

Common mistakes are memorable too:

  • Running docker compose exec web ... http://localhost:4000/health fails because localhost inside web is web's own loopback interface. The request never leaves that container.
  • Using http://api:4000/health inside the same network succeeds because Compose resolves api to the API container's network address.

Inside any container, localhost refers to that container, not your Mac. Keep the scopes straight:

  • localhost inside a container: loopback of that container
  • Talking to another container: use the service name on the shared network
  • Talking back to the host: on Docker Desktop, use names like host.docker.internal; on Linux, that name is not available by default

Quick lookup for the most confusing addresses

  • Browser → web: http://localhost:8080
  • Web container → API: http://api:4000
  • API container → DB: db:5432

Remember the mantra: localhost for the host, service names for container-to-container traffic. Part 9 explains how health checks and restart policies keep these communication paths alive.

💬 댓글

이 글에 대한 의견을 남겨주세요