✦ For everyone, free.

Practical knowledge for real and everyday life

Home

16.4.2 Compose Communication Problems

A focused guide to Compose Communication Problems, connecting core concepts with practical Docker and container operations.

Compose communication problems specifically arise from how a Compose file's own network configuration, multiple defined networks, custom names, external network references, determines which services can actually reach each other, a layer of configuration distinct from basic container-to-container networking concepts and worth troubleshooting with attention to exactly what Compose constructs by default versus what a more customized network setup explicitly defines.

The default network Compose creates automatically

Without any explicit networks configuration, Compose creates a single, default network for the entire stack and attaches every service to it automatically, which is why services in a simple Compose file can reach each other by name without any additional configuration:

services:
  api:
    image: my-api
  db:
    image: postgres
docker network ls
NETWORK ID     NAME
abc123def456   myproject_default

This implicit, automatically created network is named after the project, and every service defined in the file is attached to it by default unless a more specific, explicit network configuration overrides this behavior.

Services not sharing a network when multiple are defined

Once a Compose file defines more than one explicit network, services are no longer automatically attached to all of them; each service is only attached to whichever networks it explicitly lists, and two services that should be able to communicate but were placed on different, non-overlapping networks cannot reach each other at all:

services:
  api:
    networks:
      - frontend
  db:
    networks:
      - backend

networks:
  frontend:
  backend:
docker compose exec api ping db
ping: bad address 'db'

This failure is not a bug; it reflects the explicit network isolation the configuration actually requests, intentionally or not. The fix, if the two services genuinely need to communicate, is attaching both to a shared network:

services:
  api:
    networks:
      - frontend
      - backend
  db:
    networks:
      - backend

External network references that do not actually exist

A Compose file can reference a network as external, meaning it expects that network to already exist outside the Compose file's own management, and if that network was never actually created, or was created under a different name than expected, the stack fails to start with a clear, specific error:

networks:
  shared:
    external: true
    name: my-shared-network
network my-shared-network declared as external, but could not be found

Creating the expected network explicitly beforehand, or correcting the referenced name to match an existing network, resolves this directly:

docker network create my-shared-network
docker compose up -d

Network aliases for alternate service names

Compose supports defining additional network aliases for a service, which is useful when a dependent service or a piece of configuration expects to reach a dependency under a different hostname than its actual Compose service name:

services:
  db:
    networks:
      backend:
        aliases:
          - database
          - postgres-primary

A connection attempt to either database or postgres-primary resolves correctly to the db service through these aliases, which is useful for accommodating an application's hardcoded expectation of a specific hostname without needing to rename the actual Compose service itself.

Using network_mode to share a network namespace directly

Rather than attaching two services to a shared bridge network, network_mode: "service:other" causes one service to share the exact same network namespace as another, which means the two are effectively indistinguishable from a networking perspective, including sharing the same localhost:

services:
  api:
    image: my-api
  sidecar:
    image: my-sidecar
    network_mode: "service:api"

In this configuration, sidecar reaches api's listening ports through localhost directly, rather than through the service name over a shared bridge network, which is a meaningfully different and more tightly coupled networking relationship than ordinary service-to-service communication on a shared network, appropriate specifically for genuine sidecar patterns rather than general inter-service communication.

Verifying actual network attachment directly

When communication between two services that should be able to reach each other is failing, directly inspecting which networks each is actually attached to removes any ambiguity about whether the Compose configuration's intent matches what was actually applied:

docker inspect my-api --format '{{json .NetworkSettings.Networks}}' | jq 'keys'
docker inspect my-db --format '{{json .NetworkSettings.Networks}}' | jq 'keys'

Comparing the two lists directly confirms whether they genuinely share at least one common network, which is the prerequisite for any service-name-based communication between them to work at all, regardless of any other configuration that might otherwise appear correct.

Legacy links and why they are generally unnecessary now

Older Compose files sometimes use a links directive, a now largely unnecessary, legacy mechanism predating Compose's current automatic, network-based service discovery; in current Compose, services on a shared network already resolve each other by name without needing links at all, and the directive's presence in an older file is generally safe to remove rather than something requiring troubleshooting in its own right:

services:
  api:
    links:
      - db
services:
  api:
    # no links needed; service-name resolution works automatically for services sharing a network

Common mistakes

  • Assuming every service in a Compose file shares a single network automatically once any explicit network configuration has been introduced, without checking which specific networks each service actually lists.
  • Referencing an external network that was never actually created, or that exists under a different name than what the Compose file expects.
  • Not using network aliases when an application has a hardcoded hostname expectation that does not match its actual Compose service name.
  • Confusing network_mode: "service:other" (a tightly coupled, shared network namespace) with ordinary multi-service communication over a shared bridge network, which are meaningfully different relationships.
  • Carrying over legacy links directives from an older Compose file without recognizing they are generally unnecessary given current, automatic, network-based service name resolution.

Compose communication problems are resolved by directly comparing each service's actual network attachments against what the configuration intends, confirming external network references genuinely exist, and recognizing that once explicit networks are introduced into a Compose file, automatic, implicit shared connectivity between every service no longer applies the way it did under the simpler, single-default-network configuration.

Content in this section