Docker Compose Bridge

Written by

in

Docker Compose Bridge Networking: A Complete Configuration Guide

Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

When you run docker compose up without specifying a network, Compose quietly creates one for you using the default bridge driver. Understanding how a docker compose bridge network actually works — how containers resolve each other’s names, how ports get exposed to the host, and when you need a custom bridge instead of the default one — is essential for building reliable multi-container applications. This guide walks through the mechanics, configuration options, and common troubleshooting steps for docker compose bridge networking.

What a Docker Compose Bridge Network Actually Does

Every time you define a multi-service application in a docker-compose.yml file, Compose creates an isolated network for that project unless you tell it otherwise. This network uses the bridge driver, which is the same underlying technology as Docker’s default docker0 bridge, but scoped specifically to your Compose project. The result is a private Layer 2 network segment that only the containers in your stack can join.

The key difference between the default Compose bridge network and Docker’s global default bridge is DNS-based service discovery. Containers on a Compose-created bridge network can reach each other by service name — db, redis, api — without any manual --link flags or IP address management. This is handled by Docker’s embedded DNS server, which resolves service names to the correct container IP automatically.

How Compose Names the Default Network

By default, Compose names the network after the project directory, appended with _default. If your project lives in a folder called myapp, the network will be myapp_default. You can inspect it directly:

docker network ls | grep myapp
docker network inspect myapp_default

This naming convention matters when you have multiple Compose projects that need to communicate, or when you’re debugging why two stacks in different directories aren’t seeing each other — they’re simply on different bridge networks by design.

Bridge vs Host vs Overlay in a Compose Context

A docker compose bridge network is not the only networking mode available, but it’s the correct default for the vast majority of local and single-host deployments. Host networking removes network isolation entirely, sharing the host’s network stack directly — useful for performance-sensitive edge cases but rarely appropriate for standard web applications. Overlay networks extend across multiple Docker hosts and are relevant only in Swarm or multi-node contexts, which is outside the scope of what Compose manages on a single machine. For most projects — including the setups covered in our PostgreSQL Docker Compose guide — the bridge driver is the right choice because it provides isolation, name resolution, and controlled port exposure without the complexity of orchestrating multiple hosts.

Defining a Custom Docker Compose Bridge Network

Relying on the implicit default network works fine for small projects, but as your stack grows, an explicit networks: block gives you more control. Here’s a minimal example showing a custom bridge network with two services:

version: "3.9"

services:
  api:
    image: node:20-alpine
    working_dir: /app
    volumes:
      - ./api:/app
    command: npm start
    networks:
      - backend
    ports:
      - "3000:3000"

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: examplepassword
    networks:
      - backend
    volumes:
      - db_data:/var/lib/postgresql/data

networks:
  backend:
    driver: bridge

volumes:
  db_data:

In this configuration, the api service can reach the database at db:5432 because both services share the backend network, and Compose’s DNS resolves the hostname db to the container’s internal IP. Notice that only the api service publishes a port to the host (3000:3000) — the database stays reachable only within the bridge network, which is a good default security posture.

Custom Subnet and IPAM Configuration

Sometimes you need predictable IP ranges — for example, when integrating with firewall rules or when running multiple Compose projects that might otherwise collide on address space. You can specify IPAM (IP Address Management) settings explicitly:

networks:
  backend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/16
          gateway: 172.28.0.1

This gives you a fixed, predictable subnet for the docker compose bridge network rather than letting Docker pick one automatically from its pool, which can help avoid address conflicts on hosts running several Compose stacks simultaneously.

Assigning Static IPs to Services

For cases where a service needs a stable address inside the bridge network — some legacy applications or monitoring tools expect this — you can pin an IP within the subnet you defined:

services:
  db:
    image: postgres:16
    networks:
      backend:
        ipv4_address: 172.28.0.10

This is only possible on a custom bridge network with a defined subnet; it does not work on the default network Compose creates automatically.

Connecting Multiple Compose Projects to the Same Bridge Network

A common scenario is running separate stacks — say, an application stack and a monitoring stack — that both need to communicate. Since each docker-compose.yml normally creates its own isolated bridge network, you need to explicitly share one. Declare the network as external in the second project:

networks:
  backend:
    external: true
    name: myapp_backend

This tells Compose not to create a new network but to attach to an existing one, identified by its exact Docker-level name. You’ll need to create that network ahead of time (or let the first Compose project create it) and confirm the name with docker network ls before referencing it, since a typo here will cause the stack to fail with a “network not found” error rather than silently create a duplicate.

Bridge Networking for Reverse Proxy Setups

A frequent use case for shared docker compose bridge networks is a reverse proxy — like Caddy or Nginx — that fronts several independently deployed applications. The proxy container joins each application’s network (or a shared external one), letting it route traffic by hostname while each backend app remains isolated from the others except through the proxy. This pattern pairs well with the setup described in our Cloudflare Page Rules guide if you’re also managing DNS and caching rules at the edge.

Port Publishing and Bridge Isolation

One of the most common points of confusion is the difference between a container being reachable within the bridge network versus being reachable from the host or internet. Every service on a bridge network can talk to every other service on that same network using its internal port, regardless of whether ports: is declared. The ports: directive only controls what gets forwarded from the host machine into the container — it has no bearing on inter-container communication.

  • Omit ports: entirely for services that should only be reachable internally, like a database or cache.
  • Use expose: instead of ports: if you want to document an internal-only port without publishing it to the host.
  • Use 127.0.0.1:PORT:PORT instead of PORT:PORT when you want a service reachable from the host machine but not from the wider network — useful during local development.
  • Avoid publishing database or internal API ports to 0.0.0.0 in production unless you have a specific reason and a firewall in front of it.
  • This distinction matters for security: a common misconfiguration is publishing a database port to the host (and by extension, potentially the public internet on a VPS) when the only thing that needs to reach it is another container already sharing the same docker compose bridge network.

    Troubleshooting Bridge Connectivity Issues

    When containers can’t reach each other, the problem is almost always one of a few specific things. First, confirm both services are actually attached to the same network — a service with no networks: key joins the project’s default network, which is easy to forget if other services explicitly declare a custom one. Second, check that you’re using the service name, not localhost, to reach another container; localhost inside a container refers to that container itself, not its neighbors. Third, verify the target service is actually listening on the interface 0.0.0.0 rather than 127.0.0.1 internally — a common bug in application configs that bind only to loopback, making the port unreachable even from other containers on the same bridge.

    docker compose exec api ping db
    docker compose exec api nslookup db
    docker network inspect myapp_backend

    These three commands cover most diagnostic needs: confirming basic connectivity, confirming DNS resolution, and confirming which containers are actually attached to the network in question. If you’re also chasing down errors in container startup logs rather than networking specifically, our Docker Compose Logs debugging guide covers the relevant docker compose logs flags in more detail.

    Bridge Networks and Environment-Specific Configuration

    Because a docker compose bridge network is scoped to a single host, it behaves consistently across development, staging, and most self-hosted production setups — which is one reason Compose remains popular for smaller deployments even as teams adopt Kubernetes for larger ones. If you’re evaluating whether your project has outgrown a single-host bridge network setup, our Kubernetes vs Docker Compose comparison walks through the tradeoffs in more depth. For projects that will remain single-host, combining a well-structured bridge network with environment-specific .env files — as covered in our Docker Compose Env guide — is usually sufficient without introducing orchestration overhead.

    If you’re deploying this kind of stack on a VPS rather than locally, choosing a provider with predictable networking and enough available bandwidth matters, since bridge-networked containers routing through a reverse proxy will generate more internal traffic than a single-container deployment. Providers like DigitalOcean or Hetzner are common choices for this kind of self-hosted Compose deployment. For the official reference on network driver internals, the Docker networking documentation and the Docker Compose networking reference are the authoritative sources and worth bookmarking alongside this guide.

    Conclusion

    A docker compose bridge network gives you isolated, DNS-resolvable communication between containers with minimal configuration — in most cases, the default network Compose creates automatically is enough. Once your stack grows to include multiple projects, shared reverse proxies, or requirements around fixed IP addressing, moving to an explicit, named bridge network with custom IPAM settings gives you the control you need without switching to a heavier orchestration platform. The core rules to keep in mind: containers on the same bridge network reach each other by service name over any port, only ports: entries are exposed to the host, and external networks must be referenced by their exact Docker-level name to be shared across Compose projects.


    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    Service Discovery and DNS Resolution

    One of the most valuable features of a docker compose network bridge is built-in service discovery. Every container on the network can resolve every other container’s service name to its internal IP address through Docker’s embedded DNS resolver, which listens internally at 127.0.0.11. You never need to hardcode IP addresses in application config – just use the service name from docker-compose.yml as the hostname.

    This works because Compose registers each container’s hostname and any configured aliases with the network’s DNS layer at startup. If you scale a service to multiple replicas with docker compose up --scale api=3, the service name resolves to multiple IPs, and connections get distributed across them – a simple form of client-side load balancing without needing a separate proxy.

    Debugging DNS and Connectivity Issues

    When a service can’t reach another, the fastest diagnostic path is usually to exec into a running container and test resolution and connectivity directly:

    docker compose exec web ping -c 3 db
    docker compose exec web nslookup db
    docker compose exec web curl -v http://api:3000/health

    If ping and nslookup fail, the issue is almost always that the two services are not on the same network – a very common mistake when a service list grows and someone forgets to add a networks: entry to a new service. If DNS resolves but the connection still times out, check whether the target service is actually listening on the expected port inside the container, and whether an application-level firewall or EXPOSE mismatch is blocking it.

  • Confirm both services are attached to the same network with docker network inspect <network_name>.
  • Verify the target application is bound to 0.0.0.0, not 127.0.0.1, inside its own container.
  • Check that the port used in the connection string matches the container’s internal port, not the host-mapped port.
  • Use docker compose logs <service> to rule out a crash loop that only looks like a networking problem.
  • Inspecting and Managing Networks From the CLI

    Beyond docker compose up, a few direct Docker CLI commands are useful for verifying what Compose actually created:

    docker network ls
    docker network inspect myapp_default
    docker network disconnect myapp_default some_container

    docker network inspect is particularly valuable because it lists every container currently attached, along with its assigned IP address on that network – this is the ground truth when DNS resolution seems to be returning something unexpected. If you ever need to remove a stale network left behind after a project was renamed or restructured, docker compose down will clean up networks it created, provided no other container is still using them.

    Once you’re comfortable with the basics, it’s worth reviewing how networking interacts with other Compose features you’re already using, such as Docker Compose secrets for credentials that services on the same network need to share securely, or Docker Compose environment variables for passing connection strings between services without hardcoding hostnames.

    FAQ

    Does every Compose project need its own bridge network?
    Not necessarily. Compose creates one automatically per project by default, which is fine for isolated single-stack deployments. If two projects need to communicate, you can share a network by declaring it as external in one project’s docker-compose.yml.

    Can I connect a container on a docker compose bridge network to a service running directly on the host?
    Yes, using host.docker.internal on Docker Desktop, or by adding extra_hosts mappings on Linux. This is useful when a containerized app needs to reach a database or service running outside Docker entirely.

    Why can’t I reach my database container from outside Docker even though it’s running?
    If you haven’t declared a ports: entry for that service, it’s only reachable from other containers on the same bridge network, not from the host or the public internet. This is usually the correct behavior for internal services like databases.

    What happens to a custom docker compose bridge network when I run docker compose down?
    By default, docker compose down removes the network along with the containers, unless the network is declared external, in which case Compose leaves it untouched since it doesn’t own its lifecycle. See our Docker Compose Down guide for the full breakdown of what gets removed versus preserved.

    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *