Back to BlogDocker compose · docker · docker networks

Advanced Networking in Docker Compose

2025-12-26

Docker Compose creates isolated network environments where services can communicate securely and efficiently. While the default network configuration works for simple applications, complex architectures require custom networks, DNS configuration, and network aliases to achieve proper service isolation, multi-network connectivity, and flexible service discovery.

Understanding Default Network Behavior

When you run docker-compose up, Docker Compose automatically creates a default network for your application. All services defined in your compose file join this network and can communicate with each other using service names as hostnames.

version: '3.8'

services:
  web:
    image: nginx:alpine
    
  api:
    image: node:18-alpine
    command: node server.js

In this setup, the web service can reach the api service at http://api:3000. Docker Compose handles DNS resolution automatically, mapping service names to container IP addresses.

The default network uses the bridge driver and is named based on your project directory. If your project is in a directory called myapp, the network will be named myapp_default.

Creating Custom Networks

Custom networks provide control over network topology, isolation, and communication patterns. Define networks in the top-level networks section of your compose file.

version: '3.8'

services:
  web:
    image: nginx:alpine
    networks:
      - frontend
    
  api:
    image: node:18-alpine
    networks:
      - frontend
      - backend
    
  database:
    image: postgres:14
    networks:
      - backend

networks:
  frontend:
  backend:

This configuration creates two isolated networks. The web service can only communicate with api, while database is isolated from web. The api service bridges both networks, acting as a controlled gateway between them.

Specifying Network Drivers

Docker supports multiple network drivers, each optimized for different use cases. The most common driver for Docker Compose is bridge, which creates an isolated network on a single host.

version: '3.8'

services:
  app:
    image: myapp:latest
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

The bridge driver is the default and requires no explicit declaration. However, you can specify it explicitly for clarity or when configuring driver-specific options.

Configuring Bridge Network Options

Bridge networks support various configuration options:

networks:
  custom-bridge:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: br-custom
      com.docker.network.bridge.enable_ip_masquerade: "true"
      com.docker.network.bridge.enable_icc: "true"
      com.docker.network.bridge.host_binding_ipv4: "0.0.0.0"
    ipam:
      driver: default
      config:
        - subnet: 172.28.0.0/16
          gateway: 172.28.0.1

Driver options control low-level network behavior:

  • com.docker.network.bridge.name: Sets the Linux bridge interface name
  • com.docker.network.bridge.enable_ip_masquerade: Enables IP masquerading for external connectivity
  • com.docker.network.bridge.enable_icc: Controls inter-container communication
  • com.docker.network.bridge.host_binding_ipv4: Specifies the host IP for port bindings

IP Address Management (IPAM)

IPAM configuration controls how IP addresses are assigned to containers within a network. You can define custom subnets, gateways, and IP ranges.

networks:
  app-network:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/16
          gateway: 172.20.0.1
          ip_range: 172.20.5.0/24

This configuration creates a network with:

  • Subnet: 172.20.0.0/16 (65,536 IP addresses)
  • Gateway: 172.20.0.1
  • IP range: 172.20.5.0/24 (containers assigned IPs from this range only)

Assigning Static IP Addresses

You can assign specific IP addresses to services:

version: '3.8'

services:
  web:
    image: nginx:alpine
    networks:
      app-network:
        ipv4_address: 172.20.0.10
    
  database:
    image: postgres:14
    networks:
      app-network:
        ipv4_address: 172.20.0.20

networks:
  app-network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

Static IP assignment is useful when services need predictable addresses for configuration or when integrating with external systems that require specific IP whitelisting.

Understanding DNS Resolution

Docker Compose provides automatic DNS resolution within networks. Each service gets a DNS entry matching its service name, and all containers in the same network can resolve these names.

version: '3.8'

services:
  api:
    image: node:18-alpine
    environment:
      - DATABASE_HOST=database
      - CACHE_HOST=cache
    networks:
      - app-network
    
  database:
    image: postgres:14
    networks:
      - app-network
    
  cache:
    image: redis:alpine
    networks:
      - app-network

networks:
  app-network:

Inside the api container, you can connect to:

  • PostgreSQL at database:5432
  • Redis at cache:6379

DNS resolution happens automatically through Docker's embedded DNS server, which runs at 127.0.0.11 inside each container.

Using Network Aliases

Network aliases provide alternative DNS names for services. This is particularly useful when you need multiple names for the same service or when migrating from legacy naming schemes.

version: '3.8'

services:
  database:
    image: postgres:14
    networks:
      app-network:
        aliases:
          - db
          - postgres
          - primary-db
    
  api:
    image: node:18-alpine
    environment:
      - DB_HOST=db
    networks:
      - app-network

networks:
  app-network:

The database service is accessible via four DNS names:

  • database (service name)
  • db (alias)
  • postgres (alias)
  • primary-db (alias)

All names resolve to the same container IP address.

Multiple Networks Per Service

Services can connect to multiple networks simultaneously, enabling complex network topologies and security zones.

version: '3.8'

services:
  web:
    image: nginx:alpine
    networks:
      - public
    
  api:
    image: node:18-alpine
    networks:
      - public
      - private
    
  database:
    image: postgres:14
    networks:
      - private
    
  cache:
    image: redis:alpine
    networks:
      - private

networks:
  public:
  private:

Network isolation rules:

  • web can only reach api
  • api can reach all services
  • database and cache cannot reach web
  • database and cache can reach each other

This pattern implements a three-tier architecture where the presentation layer is isolated from the data layer.

Network Aliases Across Multiple Networks

When a service joins multiple networks, you can define different aliases for each network:

version: '3.8'

services:
  api:
    image: node:18-alpine
    networks:
      frontend:
        aliases:
          - api-gateway
          - gateway
      backend:
        aliases:
          - api-internal
          - internal-service
    
  web:
    image: nginx:alpine
    networks:
      - frontend
    
  worker:
    image: python:3.9
    networks:
      - backend

networks:
  frontend:
  backend:

The web service resolves api as api-gateway or gateway, while the worker service resolves the same container as api-internal or internal-service. This allows context-specific naming conventions.

External Networks

You can connect services to networks created outside of your compose file. This is useful when multiple compose projects need to share a network or when integrating with manually created networks.

version: '3.8'

services:
  app:
    image: myapp:latest
    networks:
      - shared-network

networks:
  shared-network:
    external: true

Before running docker-compose up, create the external network:

docker network create shared-network

Multiple compose projects can now reference this network:

Project A:

services:
  service-a:
    image: app-a:latest
    networks:
      - shared-network

networks:
  shared-network:
    external: true

Project B:

services:
  service-b:
    image: app-b:latest
    networks:
      - shared-network

networks:
  shared-network:
    external: true

Both services can communicate because they share the same network.

External Networks with Different Names

You can reference an external network using a different name in your compose file:

version: '3.8'

services:
  app:
    image: myapp:latest
    networks:
      - internal

networks:
  internal:
    external: true
    name: company-shared-network

This maps the local name internal to the actual network company-shared-network, allowing you to maintain consistent naming within your compose file while adapting to external network names.

Network Priority and Interface Order

When a service connects to multiple networks, Docker creates multiple network interfaces in the container. You can control the order using the priority option:

version: '3.8'

services:
  api:
    image: node:18-alpine
    networks:
      primary:
        priority: 1000
      secondary:
        priority: 500

networks:
  primary:
  secondary:

Higher priority values result in network interfaces being created first. This affects routing behavior when the container initiates outbound connections.

DNS Configuration Options

You can customize DNS resolution behavior for individual services:

version: '3.8'

services:
  app:
    image: myapp:latest
    dns:
      - 8.8.8.8
      - 8.8.4.4
    dns_search:
      - example.com
      - internal.local
    dns_opt:
      - ndots:2
      - timeout:3
    networks:
      - app-network

networks:
  app-network:

DNS configuration explained:

  • dns: Specifies custom DNS servers (replaces Docker's default DNS)
  • dns_search: Sets DNS search domains for unqualified hostname lookups
  • dns_opt: Configures resolver options

Warning: Setting custom dns servers bypasses Docker's DNS service, which means service name resolution may not work unless you're using external DNS servers that have entries for your services.

Using DNS Search Domains

DNS search domains automatically append domain suffixes to unqualified hostnames:

version: '3.8'

services:
  app:
    image: myapp:latest
    dns_search:
      - internal.company.com
      - company.com
    networks:
      - app-network
    
  api:
    image: node:18-alpine
    hostname: api
    domainname: internal.company.com
    networks:
      - app-network

networks:
  app-network:

Inside the app container, resolving api will try:

  1. api
  2. api.internal.company.com
  3. api.company.com

This enables shorter hostnames in configuration while maintaining fully qualified domain names for actual resolution.

Setting Hostnames and Domain Names

You can explicitly set the hostname and domain name for a service:

version: '3.8'

services:
  database:
    image: postgres:14
    hostname: primary-db
    domainname: cluster.local
    networks:
      - app-network

networks:
  app-network:

The container's FQDN becomes primary-db.cluster.local. This appears in the container's /etc/hostname and affects how the container identifies itself in logs and network communications.

Network Mode Configuration

The network_mode option provides alternative networking configurations:

version: '3.8'

services:
  # Use another service's network stack
  sidecar:
    image: monitoring-agent:latest
    network_mode: "service:web"
  
  web:
    image: nginx:alpine
    ports:
      - "80:80"

The sidecar container shares the network namespace with web, meaning:

  • They share the same IP address
  • They share the same network interfaces
  • Ports exposed in web are accessible from sidecar via localhost

Other network_mode values:

  • bridge: Default bridged networking
  • host: Use the host's network stack directly (no isolation)
  • none: Disable networking entirely

Link Options for Legacy Compatibility

Links provide an older method of container connectivity, creating environment variables and DNS entries:

version: '3.8'

services:
  web:
    image: nginx:alpine
    links:
      - api:backend-api
    
  api:
    image: node:18-alpine

networks:
  default:

The web container gets:

  • Environment variables: BACKEND_API_PORT_3000_TCP_ADDR, BACKEND_API_NAME, etc.
  • DNS entry: backend-api resolving to the api container

Note: Links are legacy features. Custom networks with service names and aliases provide better functionality and are the recommended approach.

Inter-Service Communication Patterns

Frontend-Backend-Database Pattern

version: '3.8'

services:
  nginx:
    image: nginx:alpine
    networks:
      frontend:
        aliases:
          - web
          - www
    
  application:
    image: php:8-fpm
    networks:
      frontend:
        aliases:
          - app
      backend:
        aliases:
          - api-server
    
  database:
    image: mysql:8
    networks:
      backend:
        aliases:
          - db
          - mysql

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

The internal: true option prevents the backend network from having external connectivity, enhancing security by isolating database traffic.

Microservices Communication Pattern

version: '3.8'

services:
  service-a:
    image: service-a:latest
    networks:
      service-mesh:
        aliases:
          - user-service
          - users
    
  service-b:
    image: service-b:latest
    networks:
      service-mesh:
        aliases:
          - order-service
          - orders
    
  service-c:
    image: service-c:latest
    networks:
      service-mesh:
        aliases:
          - payment-service
          - payments
    
  api-gateway:
    image: api-gateway:latest
    networks:
      public:
      service-mesh:

networks:
  public:
    driver: bridge
  service-mesh:
    driver: bridge

All microservices communicate through the service-mesh network, while external access is controlled through the api-gateway service bridging both networks.

Internal Networks for Service Isolation

Internal networks prevent containers from accessing external networks while maintaining inter-service communication:

version: '3.8'

services:
  api:
    image: node:18-alpine
    networks:
      - external-net
      - internal-net
    
  cache:
    image: redis:alpine
    networks:
      - internal-net
    
  database:
    image: postgres:14
    networks:
      - internal-net

networks:
  external-net:
    driver: bridge
  internal-net:
    driver: bridge
    internal: true

The cache and database services cannot reach the internet or the host network, but can communicate with api and each other. This provides an additional security layer for sensitive backend services.

Network Labels for Organization

Labels help document and organize networks:

networks:
  frontend:
    driver: bridge
    labels:
      com.example.tier: "presentation"
      com.example.environment: "production"
      com.example.description: "Public-facing services"
  
  backend:
    driver: bridge
    internal: true
    labels:
      com.example.tier: "data"
      com.example.environment: "production"
      com.example.description: "Internal data services"

Labels are metadata that don't affect network behavior but are useful for documentation, automation scripts, and monitoring tools.

IPv6 Network Configuration

Enable IPv6 support in your networks:

version: '3.8'

services:
  app:
    image: myapp:latest
    networks:
      ipv6-network:
        ipv6_address: 2001:db8::10

networks:
  ipv6-network:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/16
          gateway: 172.20.0.1
        - subnet: 2001:db8::/64
          gateway: 2001:db8::1

This configuration creates a dual-stack network supporting both IPv4 and IPv6. Services can use both protocols simultaneously.

MAC Address Assignment

Assign specific MAC addresses to service network interfaces:

version: '3.8'

services:
  app:
    image: myapp:latest
    mac_address: 02:42:ac:11:00:02
    networks:
      - app-network

networks:
  app-network:

Custom MAC addresses are useful for:

  • License validation systems that use MAC addresses
  • Network testing scenarios
  • Integration with systems expecting specific MAC addresses

Network Attachments Configuration

Fine-grained control over network attachments:

version: '3.8'

services:
  api:
    image: node:18-alpine
    networks:
      frontend:
        ipv4_address: 172.20.0.10
        ipv6_address: 2001:db8::10
        aliases:
          - api-gateway
        priority: 1000
      backend:
        ipv4_address: 172.21.0.10
        aliases:
          - api-internal
        priority: 500

networks:
  frontend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16
  backend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.21.0.0/16

Each network attachment can have its own IP address, aliases, and priority settings.

Default Network Configuration

Customize the default network instead of creating custom networks:

version: '3.8'

services:
  web:
    image: nginx:alpine
  
  api:
    image: node:18-alpine

networks:
  default:
    driver: bridge
    ipam:
      config:
        - subnet: 172.25.0.0/16
    driver_opts:
      com.docker.network.bridge.name: br-custom-default

This approach modifies the automatically created default network while maintaining the simplicity of not explicitly assigning networks to services.

Network Discovery Through DNS

Docker's DNS server provides automatic service discovery. Query the DNS server directly from within containers:

# Inside a container
nslookup api
dig api
getent hosts api

DNS resolution returns all IP addresses for scaled services:

version: '3.8'

services:
  api:
    image: node:18-alpine
    networks:
      - app-network

networks:
  app-network:

If you scale the API service:

docker-compose up --scale api=3

DNS queries for api return three IP addresses, enabling basic load distribution through DNS round-robin.

Network Name Customization

By default, Docker Compose prefixes network names with the project name. Override this behavior:

version: '3.8'

services:
  app:
    image: myapp:latest
    networks:
      - application-tier

networks:
  application-tier:
    name: my-custom-network-name
    driver: bridge

The network is created as my-custom-network-name instead of projectname_application-tier.

Connecting Services Across Compose Projects

Create a shared network in one project and reference it in another:

Project 1 (creates the network):

version: '3.8'

services:
  database:
    image: postgres:14
    networks:
      - shared

networks:
  shared:
    name: cross-project-network
    driver: bridge

Project 2 (uses the network):

version: '3.8'

services:
  api:
    image: node:18-alpine
    environment:
      - DB_HOST=database
    networks:
      - shared

networks:
  shared:
    external: true
    name: cross-project-network

The api service in Project 2 can communicate with the database service in Project 1 using the service name database.

Network Gateway Configuration

Specify custom gateway addresses for your networks:

networks:
  custom-network:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.30.0.0/16
          gateway: 172.30.0.254

The gateway is typically the host's interface on the bridge network. Custom gateways are useful when integrating with existing network infrastructure.

Disabling Network Creation

Prevent Docker Compose from creating networks automatically:

version: '3.8'

services:
  app:
    image: myapp:latest
    network_mode: none

# No networks section - no networks created

With network_mode: none, the container has no network access at all. This is useful for batch processing containers that don't need network connectivity.

Auxiliary Addresses in IPAM

Reserve IP addresses for non-container use:

networks:
  app-network:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.28.0.0/16
          gateway: 172.28.0.1
          aux_addresses:
            router: 172.28.0.5
            monitoring: 172.28.0.6
            vpn: 172.28.0.7

Auxiliary addresses are reserved and not assigned to containers. This prevents IP conflicts with external devices connected to the same network.

We use cookies to improve your experience and analyse site traffic. See our Privacy Policy.