Everything exposed to the internet is a part of some sort of a network. You are here reading this article, connected to the internet through ISP, and your ISP is further connected to some network upstream provider, and so on. This chain of connectivity makes networking more special and sometimes scary for people like me. Lol 😂
Disclaimer: This is not a typical blog post where you read just FORMAL writing! Ready to enjoy the ride of Docker networking? Let’s go! 🚀 Not to mention, I hope you’re ready with your coffee ☕
Earlier, we learned how to install Docker but later I didn’t cover anything. It’s been days I was learning few new things here and there but I was stuck with the docker. This article explains how the beautiful networking architecture of a docker makes it more fun to work with and, importantly, Secure!

I assume you have installed a Docker following my installation article. As soon as you install the docker, it will create a bridge network. In the below output, you can see the docker0 bridge network is created on the docker0 device.
[root@docker ~]# nmcli conn show
NAME UUID TYPE DEVICE
System eth0 149cdf48-9162-4fff-978b-5710ce3dde3a ethernet eth0
lo 0716c54b-26df-49b5-85f5-6e958fa9cd28 loopback lo
docker0 40cbbfad-d58b-4864-bcc7-bc11d9b23b42 bridge docker0
If you look at the IP configuration of our docker bridge, it is assigned with the private 172.17.0.1 IP address.
[root@docker ~]# ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:1f:91:26:3a txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
By default, whenever you spin a container, Docker will assign the container docker0 network and the private IP address from the subnet. Unless you map the local port with the container listening port, the container won’t connect to the public network. We can manage the Docker network with the docker network command.
First, let us list the available network with the docker network ls command.
[root@docker ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5d742c91037f bridge bridge local
54ef0520b348 host host local
6d093566bd29 none null local
Each network is assigned with the Network ID, and by default, the above three networks will be created.
Let us check the bridge network using the docker network inspect command. You can either use the Network ID or the Name of the network to perform an inspection. In the below example, I inspected with Network ID and grepped the Subnet and Gateway details.
[root@docker ~]# docker inspect 5d742c91037f | grep -E "Subnet|Gateway"
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
As we mentioned above, the new container will, by default be attached to the docker0 bridge, and then it will get the private IP address from the 172.17.0.0/16 subnet.
I deployed one Apache container with some basic parameters.
[root@docker ~]# docker container run -d -t -i --name apache -p 80:80 httpd
Unable to find image 'httpd:latest' locally
latest: Pulling from library/httpd
bc0965b23a04: Pull complete
d7ad38c6dd97: Pull complete
4f4fb700ef54: Pull complete
79b49624e34b: Pull complete
7d9f97915db2: Pull complete
9bd25d4f7b77: Pull complete
Digest: sha256:f4c5139eda466e45814122d9bd8b886d8ef6877296126c09b76dbad72b03c336
Status: Downloaded newer image for httpd:latest
824d7b1c6ec03f6794b4b170ac860f554688a10dabd6adb9a1637cca4af626f9
[root@docker ~]# docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
824d7b1c6ec0 httpd "httpd-foreground" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp apache
So, if I inspect the Apache container and use the “–format” to grep the “IPAddress” to find the IPv4 assigned to the container.
[root@docker ~]# docker inspect apache --format '{{ .NetworkSettings.Networks.bridge.IPAddress }}'
172.17.0.2
These are all the basics we covered so far. What’s the beauty in this?

When you are running an application, the foremost concern is the security of the application on the server and how applications talk with each other. The more public exposure you have, the more vulnerable you are on the internet. The docker network is designed to isolate the network’s unwanted interfaces to the public. As you can see, for the Apache docker container above, I specifically use “80:80“. So we are here mapping the local 80 port with the docker’s 80 port, exposing the required port from the container—no nonsense.

After the ports are mapped, all communication happens between the Bridge and the Local Server. So, when I browse http://IPADDRESS:80, the request is forwarded to the Local Server and then to the Container through Bridge. By mistake, even if you type http://IPADDRESS:8080 it won’t work ’cause we do not have any application on the localhost listening over *:8080 port.

There is a famous fellow who said (not really):
All the containers connected to the bridge network can communicate with each other. – Christopher Columbus
Let us spin another apache container with the name apache2 and let us see if it can communicate with our first apache container.
Here are out two containers:
[root@docker ~]# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a0da75a206d httpd "httpd-foreground" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp apache2
824d7b1c6ec0 httpd "httpd-foreground" 29 minutes ago Up 29 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp apache
Here are the IP addresses of two containers:
[root@docker ~]# docker container inspect apache --format '{{ .NetworkSettings.Networks.bridge.IPAddress }}'
172.17.0.2
[root@docker ~]# docker container inspect apache2 --format '{{ .NetworkSettings.Networks.bridge.IPAddress }}'
172.17.0.3
Moment of truth!
[root@docker ~]# docker container exec -it apache bash
root@824d7b1c6ec0:/usr/local/apache2# ping -c5 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.107 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.067 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.087 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.077 ms
64 bytes from 172.17.0.3: icmp_seq=5 ttl=64 time=0.086 ms
--- 172.17.0.3 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4082ms
rtt min/avg/max/mdev = 0.067/0.084/0.107/0.013 ms
root@824d7b1c6ec0:/usr/local/apache2#
Voila! Both containers can communicate with each other. 🎉 This proves Christopher Columbus was right.
The best part is that Docker allows you to create multiple networks and assign them to the containers. Why is that important? Hmm, that’s the right question. Let me explain.
There are many applications that you don’t want to push to the public interface. You can add those containers in the local docker custom bridge network and let them communicate. Still confused?
As you know, you cannot install MySQL as well as MariaDB on a single server. However, you can deploy MySQL and MariaDB containers on the same machine. If you have an application that requires MySQL can take advantage of the MySQL container, and the application that requires MariaDB can take advantage of the MariaDB container. You can create a new docker network and place your application and database server there. Then, using the private IP assigned to the containers, you can configure the SQL connectivity easily.
To show you how you can do it,
- I will create a new network.
- Spin two containers (Application + SQL).
- Assign the newly created network to the both containers.
- Configure the database connectivity.
I have created a new network “somesh-network“.
[root@docker ~]# docker network create -d bridge somesh-network
98187a575daf22bf2e19b23bdc046d2f6329cf89ace012bfd759282dc3fb0ff8
[root@docker ~]# docker network ls | grep somesh
98187a575daf somesh-network bridge local
I have deployed two containers.
- MySQL.
- Application containers (PHP + Nginx).
Let me attach the MySQL container to the “somesh-network” network.
[root@docker ~]# docker network connect somesh-network mysql
[root@docker ~]#
Check the IP address of the MySQL container from “somesh-network”.
[root@docker ~]# docker network inspect somesh-network --format '{{ .Containers }}'
map[289db7bc9cab0613b89a943ea9440b704c99f2584644fa6354ab69e0ca9858f8:{mysql af2575db7745cc5ff3866d78790d4dbbaa62224237c69e568bb21ee1279c2ebe 02:42:ac:12:00:02 172.18.0.2/16 }]
So, our MySQL is assigned with the “172.18.0.2” IP address from “somesh-network”. Cool!
The MySQL container should have two IPs. One from the default bridge network and another one from the “somesh-network” network.
[root@docker ~]# docker container inspect mysql --format '{{index .NetworkSettings.Networks "somesh-network" "IPAddress"}}'
172.18.0.2
[root@docker ~]# docker container inspect mysql --format '{{index .NetworkSettings.Networks "bridge" "IPAddress"}}'
172.17.0.2
I have removed the MySQL container from the default network.
[root@docker php_code]# docker network disconnect bridge mysql
[root@docker php_code]#
Now the MySQL container only has “somesh-network”.
[root@docker php_code]# docker container inspect mysql | grep -A50 Networks
"Networks": {
"somesh-network": {
"IPAMConfig": {},
"Links": null,
"Aliases": [],
"MacAddress": "02:42:ac:12:00:02",
"DriverOpts": {},
"NetworkID": "98187a575daf22bf2e19b23bdc046d2f6329cf89ace012bfd759282dc3fb0ff8",
"EndpointID": "af2575db7745cc5ff3866d78790d4dbbaa62224237c69e568bb21ee1279c2ebe",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"DNSNames": [
"mysql",
"289db7bc9cab"
]
}
}
}
}
]
Let us configure the SQL user in our application script.
<?php
$servername = "172.18.0.2"; // MySQL server IP
$username = "root"; // MySQL username
$password = "password"; // MySQL password
// Create connection using MySQLi
$conn = new mysqli($servername, $username, $password);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";
?>
If I curl the application from the host machine, I am getting the Connection Failed error.
[root@docker php_code]# curl http://localhost
<br />
<b>Warning</b>: mysqli::__construct(): (HY000/2002): Connection timed out in <b>/var/www/html/index.php</b> on line <b>7</b><br />
Connection failed: Connection timed out[root@docker php_code]#
This is because we have completely isolated the MySQL container with “somesh-network”. None of the containers will be able to communicate with the MySQL container unless they are part of a “somesh-network” network.
Now, let us add our application containers to the “somesh-network” network.
[root@docker ~]# docker network connect somesh-network docker-project-nginx-1
[root@docker ~]# docker network connect somesh-network docker-project-php-1
Finally, test the application now.
[root@docker ~]# curl http://localhost
Connected successfully
Congratulations! You have successfully connected the SQL with the application through a private network. 🎉🎉

Extra Tidbits:
The containers share the same network except the default bridge network; they can communicate with each other with the container name.
This is just a simple example, but there are N-numbers of possibilities for using Docker networking to establish a secure connection between containers. I really love this containerised architecture, which is easy to deploy, manage and secure.
I am still learning Docker and its related concepts. I will try to cover more interesting concepts as I learn them. Until then, thank you so much for reading my lengthy blog post, and you were the best passenger in this blog post journey!
