Docker’s networking model allows containers to communicate with each other, with the host, and with external networks in a controlled, isolated manner. Docker creates three default networks: bridge (the default for standalone containers), host (shares the host’s network namespace — maximum performance, no isolation), and none (no networking). For production applications, custom bridge networks provide DNS-based service discovery (containers can reference each other by name), better isolation between application stacks, and configurable IP ranges. Docker also supports overlay networks for multi-host container communication in Docker Swarm clusters. This guide covers Docker networking concepts, creating custom bridge networks, controlling inter-container communication, and configuring host port exposure on RHEL 9.
Prerequisites
- Docker Engine installed on RHEL 9
Step 1 — Understand Docker Network Types
# List available networks
docker network ls
# Default networks:
# bridge — default for standalone containers (no DNS discovery)
# host — no network isolation (container shares host network)
# none — no networking
Step 2 — Create a Custom Bridge Network
# Create a custom bridge network
docker network create
--driver bridge
--subnet 172.20.0.0/16
--gateway 172.20.0.1
myapp-network
docker network inspect myapp-network
Step 3 — Container-to-Container Communication
# Start a database on the custom network
docker run -d --name mydb
--network myapp-network
-e POSTGRES_PASSWORD=secret
postgres:16-alpine
# Start a web app on the same network — can reach mydb by NAME
docker run -d --name myapp
--network myapp-network
-e DATABASE_URL=postgresql://postgres:secret@mydb:5432/postgres
-p 8080:3000
myapp:latest
# Test DNS resolution
docker exec myapp ping -c 2 mydb
# Containers on DIFFERENT networks cannot communicate (isolation)
docker run --name isolated --network bridge alpine ping mydb # Fails
Step 4 — Host Port Exposure and Binding
# Publish a port: -p HOST_PORT:CONTAINER_PORT
docker run -d -p 8080:80 nginx:alpine # Listen on all host IPs on port 8080
# Bind to a specific host IP (security: only expose on a specific interface)
docker run -d -p 127.0.0.1:8080:80 nginx:alpine # Only accessible from localhost
# Bind to an IPv6 address
docker run -d -p [::1]:8080:80 nginx:alpine
Step 5 — Network in Docker Compose
# compose.yaml — explicit network configuration
services:
web:
image: nginx:alpine
networks:
- frontend
- backend
app:
build: .
networks:
- backend
db:
image: postgres:16-alpine
networks:
- backend # db is only reachable from the backend network
# NOT exposed to frontend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: false # Set to true to prevent internet access from backend network
Step 6 — Network Troubleshooting
# Inspect network and see connected containers
docker network inspect myapp-network
# Test connectivity between containers
docker exec myapp curl -s http://mydb:5432
# View container IP addresses
docker inspect --format='{{.Name}}: {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)
Conclusion
Docker networking on RHEL 9 with custom bridge networks provides DNS-based service discovery and network-level isolation between application stacks. The key security practice is binding sensitive services (databases, admin interfaces) to 127.0.0.1 rather than 0.0.0.0 when publishing ports — this ensures only processes on the host can reach them. In Docker Compose, defining separate frontend and backend networks prevents web-tier containers from directly accessing database containers, reducing the blast radius of web application vulnerabilities.
Next steps: How to Install Docker Engine on RHEL 9, How to Use Docker Volumes on RHEL 9, and How to Deploy Applications with Docker Compose on RHEL 9.