Docker Course #5: Docker Networking — Container Communication
Welcome to the Docker Course - Part 5 of 10

Source: Wikimedia Commons
Welcome back to the Docker Course! This is article 5 of 10. In the previous articles, we covered installation, images, Dockerfiles, and volumes. Now it is time to understand how containers communicate with each other and with the outside world.
Networking is essential for building real applications with Docker. Almost every application consists of multiple services — a web server, a database, a cache, a message queue — and they all need to talk to each other. Docker networking makes this possible and, as you will see, surprisingly easy.
Docker Networking Overview
When Docker is installed, it automatically creates a networking infrastructure that allows containers to communicate. Docker uses a pluggable networking subsystem with different drivers for different use cases.
Every container gets its own network namespace with its own IP address, routing table, and network interfaces. This isolation ensures that containers do not interfere with each other's networking by default.
Let's see the networks Docker creates by default:
1# List all Docker networks
2docker network ls
3
4# Output:
5NETWORK ID NAME DRIVER SCOPE
6a1b2c3d4e5f6 bridge bridge local
7f6e5d4c3b2a1 host host local
81a2b3c4d5e6f none null local
Docker comes with three built-in networks, each serving a different purpose. Let's explore them.
Network Drivers: Bridge, Host, None, and Overlay
Bridge Network (Default)
The bridge driver is the default network driver. When you run a container without specifying a network, it connects to the default bridge network. Containers on the same bridge network can communicate using IP addresses.
- Containers get an IP in the
172.17.0.0/16range by default - Containers can reach each other by IP but not by name on the default bridge
- Containers can access the internet through the host
- Ports must be explicitly published to be accessible from the host
Host Network
The host driver removes network isolation between the container and the host. The container uses the host's network stack directly.
- No port mapping needed — the container's ports are the host's ports
- Best performance (no network translation overhead)
- Only works on Linux
- Less secure due to lack of isolation
1# Run Nginx directly on the host network
2docker run -d --name nginx-host --network host nginx
3
4# Access it directly on port 80 — no -p flag needed!
5curl http://localhost:80
6
7docker rm -f nginx-host
None Network
The none driver completely disables networking for the container. The container has no external connectivity at all.
- Maximum isolation
- Use for batch jobs or security-sensitive containers that should never access the network
Overlay Network
The overlay driver enables networking between containers running on different Docker hosts (multi-host networking). It is used with Docker Swarm and Kubernetes.
- Essential for distributed applications and orchestration
- Encrypts traffic between hosts
- We will explore this in more detail in a later article about Docker Swarm
Docker Network Commands
Here are the essential commands for managing Docker networks:
1# List all networks
2docker network ls
3
4# Create a custom bridge network
5docker network create my-network
6
7# Create with specific subnet and gateway
8docker network create \
9 --driver bridge \
10 --subnet 192.168.100.0/24 \
11 --gateway 192.168.100.1 \
12 my-custom-network
13
14# Inspect a network (shows connected containers, config)
15docker network inspect my-network
16
17# Connect a running container to a network
18docker network connect my-network my-container
19
20# Disconnect a container from a network
21docker network disconnect my-network my-container
22
23# Remove a network (only if no containers are connected)
24docker network rm my-network
25
26# Remove all unused networks
27docker network prune
Custom Bridge Networks and DNS Resolution
The most important networking feature you will use daily is automatic DNS resolution on custom bridge networks. When you create a custom network, containers can find each other by name:
1# Create a custom network
2docker network create app-network
3
4# Run two containers on the same network
5docker run -d --name web-server --network app-network nginx
6docker run -d --name api-server --network app-network alpine sleep 3600
7
8# From api-server, ping web-server BY NAME
9docker exec api-server ping -c 3 web-server
10# PING web-server (172.18.0.2): 56 data bytes
11# 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.089 ms
12
13# DNS resolution works! No need to know IP addresses.
14docker exec api-server nslookup web-server
15# Name: web-server
16# Address 1: 172.18.0.2 web-server.app-network
17
18# Clean up
19docker rm -f web-server api-server
20docker network rm app-network
This is powerful because it means your application code can reference other services by a stable name (like database or redis) instead of fragile IP addresses that change every time containers restart.
Port Mapping (-p Flag)
By default, container ports are not accessible from the host or the outside world. You need to explicitly publish ports using the -p flag:
1# Map host port 8080 to container port 80
2docker run -d -p 8080:80 --name web nginx
3
4# Map to a specific host interface (only localhost)
5docker run -d -p 127.0.0.1:8080:80 --name web-local nginx
6
7# Map a range of ports
8docker run -d -p 8000-8010:8000-8010 --name multi-port my-app
9
10# Let Docker choose a random host port
11docker run -d -p 80 --name web-random nginx
12# Check which port was assigned
13docker port web-random
14# 80/tcp -> 0.0.0.0:55001
15
16# Publish all exposed ports (from EXPOSE in Dockerfile)
17docker run -d -P --name web-all nginx
18
19# View port mappings for a container
20docker port web
-p 8080:80, the port is bound to all interfaces (0.0.0.0), meaning it is accessible from anywhere that can reach your machine. For development, use -p 127.0.0.1:8080:80 to bind only to localhost.
Practical Example: Node.js App Connected to MySQL
Let's build a realistic multi-container application: a Node.js API that connects to a MySQL database through a custom Docker network.
First, create the custom network and start MySQL:
1# Create the application network
2docker network create myapp-network
3
4# Run MySQL with a named volume for data persistence
5docker volume create mysql-data
6
7docker run -d --name myapp-db \
8 --network myapp-network \
9 -e MYSQL_ROOT_PASSWORD=rootpass123 \
10 -e MYSQL_DATABASE=myapp \
11 -e MYSQL_USER=appuser \
12 -e MYSQL_PASSWORD=apppass123 \
13 -v mysql-data:/var/lib/mysql \
14 mysql:8.0
15
16# Wait about 30 seconds for MySQL to initialize, then verify
17docker exec myapp-db mysql -uappuser -papppass123 -e "SHOW DATABASES;"
Now create a simple Node.js application. Create a directory called node-mysql-demo with these files:
1{
2 "name": "node-mysql-demo",
3 "version": "1.0.0",
4 "scripts": { "start": "node index.js" },
5 "dependencies": {
6 "express": "^4.18.2",
7 "mysql2": "^3.6.0"
8 }
9}
Create index.js:
1# index.js
2const express = require('express');
3const mysql = require('mysql2/promise');
4
5const app = express();
6const PORT = 3000;
7
8// Notice: we use the container NAME 'myapp-db' as the host!
9const dbConfig = {
10 host: process.env.DB_HOST || 'myapp-db',
11 user: process.env.DB_USER || 'appuser',
12 password: process.env.DB_PASS || 'apppass123',
13 database: process.env.DB_NAME || 'myapp'
14};
15
16app.get('/', async (req, res) => {
17 try {
18 const conn = await mysql.createConnection(dbConfig);
19 const [rows] = await conn.execute('SELECT NOW() as currentTime');
20 await conn.end();
21 res.json({
22 message: 'Connected to MySQL via Docker network!',
23 dbTime: rows[0].currentTime
24 });
25 } catch (err) {
26 res.status(500).json({ error: err.message });
27 }
28});
29
30app.get('/health', (req, res) => {
31 res.json({ status: 'healthy' });
32});
33
34app.listen(PORT, () => {
35 console.log('API running on port ' + PORT);
36});
Create the Dockerfile:
1FROM node:20-alpine
2WORKDIR /app
3COPY package.json package-lock.json ./
4RUN npm ci --only=production
5COPY . .
6ENV NODE_ENV=production
7EXPOSE 3000
8USER node
9CMD ["node", "index.js"]
Build and run the Node.js app on the same network:
1# Build the Node.js image
2docker build -t node-mysql-demo .
3
4# Run the app on the same network as MySQL
5docker run -d --name myapp-api \
6 --network myapp-network \
7 -p 3000:3000 \
8 node-mysql-demo
9
10# Test the connection
11curl http://localhost:3000
12# {"message":"Connected to MySQL via Docker network!","dbTime":"2026-04-05T12:00:00.000Z"}
13
14# The Node.js container resolves 'myapp-db' to the MySQL container's IP
15# automatically via Docker's built-in DNS!
The key takeaway here is that the Node.js application connects to MySQL using the container name myapp-db as the hostname. Docker's built-in DNS resolver on the custom network handles the name-to-IP resolution automatically.
-e DB_HOST=myapp-db -e DB_USER=appuser -e DB_PASS=secret. Never hardcode passwords in your application code.
Clean up everything:
1# Remove containers
2docker rm -f myapp-api myapp-db
3
4# Remove the network
5docker network rm myapp-network
6
7# Optionally remove the volume and image
8docker volume rm mysql-data
9docker rmi node-mysql-demo
Network Inspection and Debugging
When things go wrong with container networking, these commands help you diagnose the issue:
1# Inspect a network — shows all connected containers and their IPs
2docker network inspect myapp-network
3
4# Check a container's network settings
5docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-container
6
7# View all networks a container is connected to
8docker inspect --format='{{json .NetworkSettings.Networks}}' my-container | python -m json.tool
9
10# Test connectivity from inside a container
11docker exec -it my-container ping other-container
12docker exec -it my-container nslookup other-container
13docker exec -it my-container wget -qO- http://other-container:3000
14
15# Check which ports are published
16docker port my-container
17
18# Use a dedicated networking debug container
19docker run -it --rm --network myapp-network nicolaka/netshoot
20# This image includes curl, dig, nslookup, ping, traceroute, iperf, etc.
localhost or 127.0.0.1 — inside a container, localhost refers to the container itself, not the host or other containers.
Network Security Best Practices
Follow these best practices to keep your Docker networking secure:
- Use custom networks: Never rely on the default bridge network. Create dedicated networks for each application stack.
- Isolate by function: Put your database on a private network that only the backend can access. Do not expose database ports to the host unless needed for debugging.
- Bind to localhost: In development, use
-p 127.0.0.1:PORT:PORTinstead of-p PORT:PORT. - Use read-only containers: Add
--read-onlyfor containers that do not need to write to the filesystem. - Limit network access: Use the
--internalflag to create networks with no external access:docker network create --internal private-net. - Do not publish unnecessary ports: Only expose the ports your application actually needs.
1# Example: secure network architecture
2# Public network — only the reverse proxy is exposed
3docker network create public-net
4
5# Private network — backend and database only
6docker network create --internal private-net
7
8# Reverse proxy: connects to both networks
9docker run -d --name proxy --network public-net -p 80:80 nginx
10docker network connect private-net proxy
11
12# Backend API: only on private network (not directly accessible from host)
13docker run -d --name api --network private-net my-api
14
15# Database: only on private network, no port publishing
16docker run -d --name db --network private-net postgres:16-alpine
For the complete reference on Docker networking, see the official Docker networking documentation.
Summary
In this fifth article of the Docker Course, we covered:
- Docker networking overview and how containers get their own network namespaces
- Network drivers: bridge, host, none, and overlay — when to use each
- Essential network commands: create, ls, inspect, connect, disconnect, rm
- Custom bridge networks with automatic DNS resolution by container name
- Port mapping with the
-pflag and binding to specific interfaces - A practical multi-container example: Node.js connected to MySQL via a custom network
- Debugging network issues with inspect, ping, nslookup, and netshoot
- Security best practices for Docker networking
In the next article (Part 6 of 10), we will learn about Docker Compose — a tool that lets you define and manage multi-container applications with a single YAML file, making everything we did manually in this article much simpler and reproducible. See you there!
Comments
Sign in to leave a comment
No comments yet. Be the first!