Docker Networking Explained: How Containers Really Talk

Docker Networking Explained: How Containers Really Talk

Docker networking allows containers to communicate with each other as well as with external applications. By default, every container has networking enabled, but it doesn’t automatically know about other containers running on the same host or elsewhere. Understanding how Docker networks work is crucial for building scalable and secure containerized applications.

There are 6 types of network drivers available in Docker, which provide different functionalities.

  • bridge: The default network driver.
  • host: Removes the network isolation between the container and host.
  • none: Completely isolates a container from the host and other containers.
  • overlay: Connects multiple Docker daemons.
  • ipvlan: Provides full control over the IPv4 and IPv6 addressing.
  • macvlan: Assigns a MAC address to a container.

In this blog, we are going to examine the bridge network, which is the default network.

By default, every container is connected to a default network called Bridge Network when a custom network isn't provided at the time of running the container. Containers can talk to each other by connecting to the same bridge network. To inspect the bridge network, execute the command docker inspect bridge

user:~$ docker inspect bridge
[
    {
        "Name": "bridge",
        "Id": "98de60e172e605a303e18c87e6cb5f5effa4adbe6c3fdbdc99ce85b8f5c4a77a",
        "Created": "2025-09-28T09:24:41.548659308Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv4": true,
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]        

The above output from the command shows the bridge network configs. If you look at the output, the network has its own subnet: 172.17.0.0/16, gateway: 172.17.0.1. Containers exist, but are not connected to host traffic directly.

Now, it's time to experiment with some containers. Let's quickly spin up two Apache:httpd containers with the names apache1 and apache2. Execute the commands docker run -d --name apache1 httpd:alpine, and docker run -d --name apache2 httpd:alpine. Verify they are up and running by executing the command docker ps.

user:~$ docker ps
CONTAINER ID   IMAGE          COMMAND              CREATED         STATUS         PORTS     NAMES
9325e8181443   httpd:alpine   "httpd-foreground"   2 minutes ago   Up 2 minutes   80/tcp    apache2
983f3e6aa23f   httpd:alpine   "httpd-foreground"   2 minutes ago   Up 2 minutes   80/tcp    apache1        

Because we didn't specify any custom network while running the containers, they are attached to the default bridge network. Verify it by executing the command docker inspect bridge

user:~$ docker inspect bridge
[
    {
        "Name": "bridge",
        "Id": "dd1700e5c4accd1b534b2d4f0c9774d838afd659d30d72c7969666b8ade172b1",
        "Created": "2025-09-28T09:36:32.342353794Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv4": true,
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "9325e81814435890479887a4f7b433b94154704004e37e443d9e2ce40e812c3c": {
                "Name": "apache2",
                "EndpointID": "e14a6a72a2dcd016bbed5423734525692c561a2496e622c243a720a4d947ff5d",
                "MacAddress": "96:4b:a6:e3:b0:bb",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "983f3e6aa23f34914bc649f5dead979221e7220c6cc612dbf84345b75773b285": {
                "Name": "apache1",
                "EndpointID": "991978701adc991a93789ddfbaac81202a0380a18c78a12e0198b28a1e01d88f",
                "MacAddress": "9a:0a:d7:78:b5:5f",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]        

We can see the containers apache1 and apache2 in the bridge network with IP addresses 172.17.0.2 and 172.17.0.3. Because they are under the same network, they should be able to reach each other. Let's verify it by entering one of the container shells and ping the other.

Execute the command docker exec -it apache1 sh to enter inside the apache1 container shell and ping the other ping 172.17.0.3

user:~$ docker exec -it apache1 sh
/usr/local/apache1 # ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.230 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.158 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.175 ms
64 bytes from 172.17.0.3: seq=3 ttl=64 time=0.220 ms
64 bytes from 172.17.0.3: seq=4 ttl=64 time=0.225 ms
64 bytes from 172.17.0.3: seq=5 ttl=64 time=0.207 ms
64 bytes from 172.17.0.3: seq=6 ttl=64 time=0.182 ms
64 bytes from 172.17.0.3: seq=7 ttl=64 time=0.185 ms
64 bytes from 172.17.0.3: seq=8 ttl=64 time=0.182 ms
64 bytes from 172.17.0.3: seq=9 ttl=64 time=0.254 ms
64 bytes from 172.17.0.3: seq=10 ttl=64 time=0.236 ms
64 bytes from 172.17.0.3: seq=11 ttl=64 time=0.189 ms
64 bytes from 172.17.0.3: seq=12 ttl=64 time=0.223 ms
64 bytes from 172.17.0.3: seq=13 ttl=64 time=0.231 ms
64 bytes from 172.17.0.3: seq=14 ttl=64 time=0.231 ms
64 bytes from 172.17.0.3: seq=15 ttl=64 time=0.180 ms
^C
--- 172.17.0.3 ping statistics ---
16 packets transmitted, 16 packets received, 0% packet loss
round-trip min/avg/max = 0.158/0.206/0.254 ms        

We can see that we can reach Apache2 from Apache1; it is possible because they are connected to the same network. Now, let's try to ping one of the servers from the host.

ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
^C
--- 172.17.0.3 ping statistics ---
61 packets transmitted, 0 received, 100% packet loss, time 61352ms         

Because the bridge network is isolated from the host network, we are unable to reach the servers inside from our host. To access the server that is running inside the isolated bridge network from the host, we have to publish the port of that server (application) to the host. Now, let's spin up another server which defaults to a bridge network, but this time we publish the port it's running on to the host network. docker run -d --name apache3 -p 80:80 httpd:alpine, this command spins up another server with the container name apache3 and publishes the port it's running on to the host network port 80.

Let's go to our browser and type in localhost:80 in the URL. This will show us the default server HTML page. What happened here is that the first two containers are communicating within their own network, but the third is communicating between different networks through the default bridge gateway, which is 172.17.0.1.

We can also create multiple custom networks, which are all isolated from one another. Containers can also connect across different bridge networks, making it possible for them to communicate between different bridge networks.

Article content
Containers communicating with their peers and non-Docker applications
“Keep exploring, keep coding, and stay curious!”

To view or add a comment, sign in

More articles by Rakesh Noothi

  • When Sharding Breaks: How Consistent Hashing Saves Your System

    The application you helped build is gaining popularity. On a quiet Saturday at the beach—your eyes on the orange sky…

  • Memory alignment of a struct in GO Lang

    Memory alignment is the practice of storing data at memory addresses that are multiples of the data size, enabling the…

  • Intro to Kafka

    What is even APACHE KAFKA? Apache Kafka is an open-source distributed event streaming platform used to build real-time,…

  • Go Channels Explained: The Simple Way Goroutines Talk

    What are even channels? Channels in Go are like pipes that connect goroutines. They allow you to safely send and…

  • The HTTP

    Ever wondered how your browser fetches a webpage or how data flows between servers? Understanding HTTP is key to…

Explore content categories