docker.recipes
Networking9 min read

Docker Networks Explained for Self-Hosting

Understand how Docker networking works and how to configure networks for your self-hosted services.

01Introduction

Docker networking often confuses beginners. Why can't containers reach each other? Why does localhost not work as expected? This guide demystifies Docker networks and shows you how to configure them properly for self-hosting.

02The Default Network

Docker Compose creates a default network for each project. All services in the same compose file automatically join it and can reach each other by service name.
1# docker-compose.yml in folder "myapp"
2services:
3 web:
4 image: nginx
5 # Can reach db at hostname "db"
6
7 db:
8 image: postgres
9 # Can reach web at hostname "web"
10
11# Docker automatically creates network "myapp_default"
12# Both containers join it automatically

Service names become hostnames. If your service is named db, other containers reach it at db:5432.

03Custom Networks

Define custom networks to control which services can communicate. Services only on the same network can reach each other.
1services:
2 traefik:
3 image: traefik:v3.0
4 networks:
5 - frontend
6 - backend
7
8 app:
9 image: myapp:1.0
10 networks:
11 - backend
12 - database
13
14 db:
15 image: postgres:15
16 networks:
17 - database # Only app can reach db
18
19networks:
20 frontend:
21 name: traefik-public
22 backend:
23 name: myapp-backend
24 database:
25 name: myapp-database

04Internal Networks

Mark networks as internal to prevent containers from accessing the internet. Ideal for databases and backend services.
1services:
2 app:
3 networks:
4 - external # Can reach internet
5 - internal # Can reach db
6
7 db:
8 networks:
9 - internal # Cannot reach internet
10
11networks:
12 external:
13 driver: bridge
14 internal:
15 driver: bridge
16 internal: true # No internet access

Containers on internal networks cannot pull updates or reach external APIs. Plan accordingly.

05Cross-Project Communication

Services in different compose files don't share networks by default. Use external networks to connect them.
1# First: Create a shared network
2# docker network create shared-services
3
4# traefik/docker-compose.yml
5services:
6 traefik:
7 networks:
8 - shared
9
10networks:
11 shared:
12 external: true
13 name: shared-services
14
15# app/docker-compose.yml
16services:
17 app:
18 networks:
19 - shared
20 labels:
21 - "traefik.enable=true"
22
23networks:
24 shared:
25 external: true
26 name: shared-services
27
28# Both projects can now communicate on "shared-services"

Create the external network before running docker compose up, or the command will fail.

06Network Aliases

Give services multiple hostnames using aliases. Useful when migrating or when different services expect different hostnames.
1services:
2 database:
3 image: postgres:15
4 networks:
5 backend:
6 aliases:
7 - db
8 - postgres
9 - database-server
10
11networks:
12 backend:
13
14# Other containers can reach this as:
15# - database (service name)
16# - db
17# - postgres
18# - database-server

07Static IP Addresses

Usually unnecessary, but sometimes you need a fixed IP. Define a subnet and assign addresses.
1services:
2 app:
3 networks:
4 fixed:
5 ipv4_address: 172.20.0.10
6
7 db:
8 networks:
9 fixed:
10 ipv4_address: 172.20.0.20
11
12networks:
13 fixed:
14 driver: bridge
15 ipam:
16 config:
17 - subnet: 172.20.0.0/24
18 gateway: 172.20.0.1

Avoid static IPs unless absolutely necessary. Service names are more maintainable and work across environments.

08DNS Resolution

Docker provides automatic DNS resolution within networks. Containers find each other by service name. Custom DNS servers can be configured if needed.
1services:
2 app:
3 dns:
4 - 1.1.1.1
5 - 8.8.8.8
6 dns_search:
7 - example.com
8
9# Or configure at network level
10networks:
11 custom:
12 driver: bridge
13 driver_opts:
14 com.docker.network.bridge.enable_dns: "true"

09Debugging Network Issues

When containers can't communicate, use these commands to diagnose the problem.
1# List all networks
2docker network ls
3
4# Inspect a network (see connected containers)
5docker network inspect myapp_default
6
7# Check which networks a container is on
8docker inspect myapp --format '{{json .NetworkSettings.Networks}}' | jq
9
10# Test connectivity from inside a container
11docker exec app ping db
12docker exec app nc -zv db 5432
13
14# Run a debug container on the network
15docker run --rm -it --network myapp_default alpine sh
16# Then: ping db, nc -zv db 5432, etc.

The alpine image is perfect for debugging—small and has basic network tools. Install more with apk add curl bind-tools.

10Network Modes: Bridge vs Host

Bridge mode (default) provides isolation. Host mode removes network isolation—containers share the host's network stack.
1services:
2 # Bridge mode (default) - isolated network
3 app:
4 image: myapp:1.0
5 networks:
6 - app-network
7 ports:
8 - "8080:80"
9
10 # Host mode - no isolation, uses host network directly
11 # Container binds directly to host ports
12 monitoring:
13 image: prometheus:latest
14 network_mode: host
15 # No port mapping needed - binds to host:9090 directly
16
17# Host mode use cases:
18# - Performance-critical applications
19# - Services that need to see all host traffic
20# - When you need to bind to many ports

Host mode bypasses Docker's network isolation. The container has full access to the host network.