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.