Docker Lesson 26 – Docker Compose Networking | Dataplexa
Section III · Lesson 26

Docker Compose Networking

Compose creates a network automatically — that's what makes service-name DNS work out of the box. But the moment your application grows beyond one Compose file, or you need services in different stacks to talk to each other, you need to understand how Compose networking really works.

This lesson covers the default network Compose creates, how to connect services across different Compose projects, how to use existing external networks, and the networking patterns that appear in every serious production deployment.

The Default Network Compose Creates

When you run docker compose up without defining any networks, Compose automatically creates a single bridge network named after the project. The project name defaults to the directory name — so if your project lives in a folder called order-api, the network is named order-api_default.

Every service in the Compose file is attached to this default network automatically. Every service can reach every other service by name. This is why a freshly written Compose file just works — Compose wires up DNS for free without any network configuration on your part.

# See which networks Compose created for your project
docker network ls | grep order-api

# Inspect the default network
docker network inspect order-api_default
NETWORK ID     NAME                  DRIVER    SCOPE
9e1f3a5b7c9d   order-api_default     bridge    local

[
  {
    "Name": "order-api_default",
    "Driver": "bridge",
    "Containers": {
      "a1b2...": { "Name": "order-api-db-1",    "IPv4Address": "172.18.0.2/16" },
      "b3c4...": { "Name": "order-api-redis-1", "IPv4Address": "172.18.0.3/16" },
      "c5d6...": { "Name": "order-api-api-1",   "IPv4Address": "172.18.0.4/16" }
    }
  }
]

What just happened?

Compose created order-api_default and attached all three containers to it. Each container got its own IP in the 172.18.0.0/16 subnet. The container names — order-api-db-1, order-api-redis-1, order-api-api-1 — are also their DNS hostnames on this network. Notice the prefix order-api — that's the project name, derived from the directory. If two developers have the same project in differently named folders, their networks won't conflict.

Connecting Two Compose Projects

The default network is isolated — containers in one Compose project cannot reach containers in another by default. This is usually the right behaviour. But there are real cases where you need cross-project communication — a shared database stack used by multiple application stacks, or a monitoring stack that needs to reach every other stack's containers.

The solution is an external network — a Docker network created outside of any Compose project that multiple projects can join.

The scenario: Your organisation runs a shared infrastructure stack — PostgreSQL and Redis — used by multiple application teams. Each team's Compose file connects to the shared databases via an external network rather than running their own copies.

# Step 1 — Create the shared network manually (done once, by the infra team)
docker network create shared-infra-net
# docker-compose.yml for the SHARED INFRASTRUCTURE stack (infra team)
services:

  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - shared-infra-net    # attached to the shared external network

  redis:
    image: redis:7-alpine
    networks:
      - shared-infra-net

networks:
  shared-infra-net:
    external: true          # this network was created outside this Compose file
    # Compose will NOT create or delete this network — it just uses it

volumes:
  postgres-data:
# docker-compose.yml for the ORDER API stack (application team)
services:

  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://postgres:${DB_PASSWORD}@postgres:5432/orders
      REDIS_URL: redis://redis:6379
    networks:
      - default            # api's own default network (for internal services)
      - shared-infra-net   # also on the shared network to reach postgres and redis

networks:
  shared-infra-net:
    external: true         # same external network — joins it, doesn't own it
# After starting both stacks:
docker network inspect shared-infra-net

"Containers": {
  "a1b2...": { "Name": "infra-postgres-1", "IPv4Address": "172.20.0.2/16" },
  "b3c4...": { "Name": "infra-redis-1",    "IPv4Address": "172.20.0.3/16" },
  "c5d6...": { "Name": "order-api-api-1",  "IPv4Address": "172.20.0.4/16" }
}
# All three containers — from two different Compose projects — on the same network
# order-api-api-1 can reach infra-postgres-1 by the hostname "postgres"

What just happened?

Three containers from two completely different Compose projects are sharing one network. The API container from the order-api project can now connect to PostgreSQL using the hostname postgres — even though PostgreSQL lives in a different Compose project. The external network acts as a shared corridor between isolated projects. The external: true key in both Compose files tells Compose "this network belongs to nobody — just join it." Neither Compose file creates or deletes it. The infra team manages the network lifecycle independently.

Cross-project networking via external network

infra project

postgres
redis
shared-infra-net
(external network)
DNS works across projects

order-api project

api
worker

Both projects join the same external network. Containers reach each other by service name. Neither project owns the network lifecycle.

Network Aliases in Compose

Just like the --network-alias flag in docker run, Compose services can have aliases — alternative hostnames on a specific network. This is useful when you need a service to be reachable under a different name on one network than another, or when migrating service names without breaking dependent services.

services:

  order-api:
    build: .
    networks:
      backend-net:
        aliases:
          - api              # reachable as "api" on backend-net
          - order-service    # also reachable as "order-service" on backend-net
      frontend-net:
        aliases:
          - api-public       # different alias on the frontend network

  payment-api:
    build: ./payment
    networks:
      backend-net:
        aliases:
          - payment          # reachable as "payment" on backend-net

networks:
  backend-net:
    driver: bridge
  frontend-net:
    driver: bridge
# From inside any container on backend-net — all three resolve to order-api:
# curl http://order-api:3000/health   → 200 OK
# curl http://api:3000/health         → 200 OK
# curl http://order-service:3000/health → 200 OK

# From inside any container on frontend-net:
# curl http://api-public:3000/health  → 200 OK
# curl http://order-api:3000/health   → 200 OK (container name always works too)

What just happened?

The order-api service now responds to three hostnames on backend-net — its service name, plus two aliases. This is valuable during migrations: when renaming a service from order-api to api, you can add the new alias first, migrate all dependent services to use the new name, then remove the old alias — zero downtime renaming. Aliases are network-specific — api-public only works on frontend-net, not on backend-net.

Controlling the Project Name

The project name controls all Compose resource naming — containers, networks, and volumes all get the project name as a prefix. By default it's the directory name, which can cause collisions if multiple team members clone the same repo into different directories, or if you run the same project in two environments on one machine.

# Set the project name explicitly — overrides the directory name
docker compose -p my-order-api up -d
# All resources named: my-order-api_default, my-order-api-api-1, etc.

# Or set it in a .env file (Compose reads this automatically)
# COMPOSE_PROJECT_NAME=my-order-api

# Or set the name in docker-compose.yml at the top level
# name: my-order-api   ← top-level key in the Compose file

# Check the project name of a running stack
docker compose ls
NAME             STATUS     CONFIG FILES
my-order-api     running    /home/dev/projects/order-api/docker-compose.yml
infra            running    /home/dev/infra/docker-compose.yml

What just happened?

docker compose ls shows all running Compose projects on the machine — the project name, status, and the Compose file path. Setting a project name explicitly with -p ensures all resources are named consistently regardless of which directory the project was started from. This is essential in CI/CD environments where the same project might be cloned to differently named workspace directories on different build agents.

Network Naming Collisions

Two developers both clone the same repo into a folder named app. Both run docker compose up. Both create a network named app_default. The second one fails because the network name is taken. Always set an explicit project name — either with -p, the name key in the Compose file, or COMPOSE_PROJECT_NAME in the .env — on any project that multiple people or processes will run on the same machine.

Teacher's Note

External networks are the cleanest solution when you have shared infrastructure — one Compose file owns the resources, all others just join. Never duplicate a database stack across multiple Compose files if they're on the same machine.

Practice Questions

1. The default Compose network is named after the what — which by default comes from the directory name?



2. To connect a Compose service to a Docker network that was created outside of any Compose project, you declare the network with which key set to true?



3. The Compose network key that lets you give a service additional hostnames on a specific network is called what?



Quiz

1. Two separate Compose projects need their containers to communicate. The correct approach is:


2. Two developers each clone the same repo into a folder named app and both run docker compose up on the same machine. What happens?


3. To see all currently running Compose projects on a machine — their names, status, and config file paths — which command do you run?


Up Next · Lesson 27

Docker Compose Volumes

Networks sorted — now let's go deep on how Compose manages volumes, how to share volumes across services, and the patterns that keep your data safe across the full deployment lifecycle.