Yar Kravtsov's blog

Published at

Docker Networking: A Guide for Developers

Table of Contents

Docker networking is a foundational concept for building and deploying containerized applications. It allows containers to communicate with each other, the host machine, and external networks like the internet. While tools like docker-compose simplify multi-container setups, they often abstract key networking details that developers need to understand for debugging and configuration.

This guide builds a step-by-step understanding of Docker networking, focusing on DNS, aliases, port forwarding, and internal/external container communication. By the end, you’ll grasp how Docker networks work and avoid common pitfalls.


What is a Docker Network?

A Docker network is a virtual layer that connects containers, enabling communication within a network. Each container in a network has its own unique IP address and is configured to work seamlessly with other containers on the same network. Docker abstracts away much of the complexity, but understanding its principles is crucial for effective application design.

Core Properties of Docker Networks

  1. Container Communication: Containers on the same network can communicate directly using:
    • The container’s IP address (assigned by Docker).
    • The container’s name or alias, resolved via Docker’s built-in DNS system.
  2. Port Exposure: Containers do not expose their services to the host or external networks by default. You must explicitly forward ports for external access.
  3. DNS Integration: Containers in the same network can resolve each other by name or alias, making service discovery simple.

How Containers Communicate in a Network

When containers are on the same Docker network, they can directly connect to each other using internal ports. These ports correspond to the services running inside the container.

Inside a Network: Internal Ports

For containers within the same network:

  • Communication happens directly via the port the application is running on inside the container.
  • Port mappings like -p 8080:3000 are irrelevant for internal communication—these mappings are only for external traffic.

Example:

If a container app is running a service on port 3000:

  • Another container on the same network can connect to it using app:3000.
  • The external mapping (e.g., -p 8080:3000) is not needed for this internal communication.

DNS and Service Names

Docker provides an internal DNS system for each network, allowing containers to resolve each other by their names or aliases.

Container Names

  • A container’s name is its default DNS hostname.
  • For example, if a container is named db, another container in the same network can connect to it using db as the hostname.

Aliases

  • Aliases are additional names for a container within a network. They allow flexibility when naming containers.
  • For instance:
    docker run --network my-network --name db --network-alias primary-db mydatabase
    
    Here:
    • The container is reachable as db (default name).
    • It is also reachable as primary-db (alias).

Multi-Container Setup

Tools like docker-compose automatically create service names as DNS entries. For example:

services:
  app:
    image: myapp
  db:
    image: mydatabase
  • The app container can connect to the db container by simply using db:5432, assuming the database runs on port 5432.

Exposing Services with Port Forwarding

Docker uses port forwarding to expose container services to the host machine or external networks. This is necessary when you want to access a service running in a container from outside the Docker network.

How Port Forwarding Works

When forwarding a port, you map a container’s internal port to a port on the host:

docker run -p 8080:3000 mywebapp
  • 3000: The port inside the container where the application is running.
  • 8080: The port on the host machine where the service is exposed.

Requests to http://localhost:8080 are forwarded to port 3000 inside the container.

Internal vs. External Ports

  • Internal container communication uses internal ports (e.g., 3000).
  • External systems use the host-mapped port (e.g., 8080).

In Docker Compose

Port mappings in docker-compose.yml are specified as follows:

services:
  web:
    build: .
    ports:
      - "8080:3000"
  • This maps port 3000 inside the web container to port 8080 on the host.
  • Other containers in the same network still access the web container using its internal port (3000).

Understanding Internal and External Access

Internal Access

Containers on the same network communicate directly using the container’s name (or alias) and the port the service listens on inside the container. For example:

  • A web server in webapp listening on 3000 is accessed as webapp:3000 by another container.

External Access

To make a container’s service accessible to the host or the internet:

  1. Map the container’s port to a host port (e.g., -p 8080:3000).
  2. Access the service through the host’s port (e.g., http://localhost:8080).

Bringing It All Together: A Practical Example

Let’s walk through a multi-container application where a web app communicates with a database.

Scenario:

  • The web app listens on port 5000.
  • The database listens on port 5432.

Steps:

  1. Create a Docker network:

    docker network create my-app-network
    
  2. Start the database container:

    docker run --network my-app-network --name db --network-alias primary-db mydatabase
    
    • Accessible as db:5432 (container name).
    • Also accessible as primary-db:5432 (alias).
  3. Start the web app container:

    docker run --network my-app-network --name webapp mywebapp
    
  4. Communication:

    • The web app connects to the database using db:5432 or primary-db:5432.
  5. Expose the web app to the host:

    docker run -p 8080:5000 --network my-app-network --name webapp mywebapp
    
    • External users access the web app at http://localhost:8080.

Key Takeaways

  1. Internal Ports Matter: Inside a network, containers communicate using the port the application runs on inside the container (e.g., 3000), not the mapped host port.
  2. DNS Simplifies Communication: Containers in the same network can use names or aliases to resolve each other, thanks to Docker’s DNS.
  3. Port Mappings Are for External Access: Host-to-container or internet-to-container communication requires port forwarding (e.g., 8080:3000).
  4. Service Names in Compose: In docker-compose, service names automatically act as DNS entries for internal communication.

With these principles in mind, you’ll have the tools to confidently configure and debug Docker networks in any containerized application.

  • Yar Kravtsov
    Name
    Yar Kravtsov
    About
    Senior Software Engineer
    Twitter
    @yarlson