In modern application development, Docker plays a crucial role by allowing services to be isolated into individual containers. However, these services often need to communicate with each other or with the external world (Internet). In this blog, we will explore container-to-external communication, container-to-local machine communication, and cross-container communication using networks — all with practical examples.
A Practice Application
We will use a simple Node.js application that has the following dependencies:
axios
express
body-parser
mongoose
The app exposes 4 endpoints:
-
GET /favorites
— Fetch favorite movies from local MongoDB -
POST /favorites
— Add a favorite movie to local MongoDB -
GET /movies
— Fetch movie data from an external third-party API -
GET /people
— Fetch people data from an external third-party API
External API reference:
Step 1: Building and Running the Container
First, build the Docker image:
docker build -t favorites-node .
-
docker build
: Command to build the image. -
-t favorites-node
: Tagging the image with the namefavorites-node
. -
.
: Context is the current directory.
Next, run the container:
docker run --name favorites --rm -p 3000:3000 favorites-node
-
--name favorites
: Give a name to the container. -
--rm
: Automatically remove the container after it exits. -
-p 3000:3000
: Map port 3000 of the container to port 3000 of the local machine. -
favorites-node
: The image to run.
Step 2: Issue With Local MongoDB
When the application tries to connect to the local MongoDB:
mongoose.connect(
'mongodb://localhost:27017/swfavorites',
{ useNewUrlParser: true },
(err) => { /* callback */ }
);
It fails because localhost inside the container refers to the container itself, not your local machine.
If you comment out the MongoDB connection code and run the container, the /movies
and /people
endpoints work because they connect to the external world without any issues. This proves containers can connect to the Internet by default.
Step 3: Connect to Local Machine From Container
To fix the MongoDB connection issue, update the connection string to:
mongoose.connect(
'mongodb://host.docker.internal:27017/swfavorites',
{ useNewUrlParser: true },
(err) => { /* callback */ }
);
Here, host.docker.internal
points to your local machine from inside the Docker container.
Now running the container again will successfully connect the Node.js app inside the container to MongoDB running on your local machine.
Step 4: Running MongoDB as a Container
Instead of relying on a local MongoDB server, let's run MongoDB in its own container.
docker run -d --name mongodb mongo
-
-d
: Run in detached mode (in the background). -
--name mongodb
: Name the container. -
mongo
: Use the official MongoDB Docker image.
Now inspect the MongoDB container:
docker container inspect mongodb
Look for the IPAddress
field. Suppose it is 172.17.0.2
.
Update the Node.js connection:
mongoose.connect(
'mongodb://172.17.0.2:27017/swfavorites',
{ useNewUrlParser: true },
(err) => { /* callback */ }
);
This works because now the app container can reach the MongoDB container via its IP.
Step 5: Simplifying With Docker Networks
Manually managing IPs is tedious. Instead, we can create a Docker network.
Steps:
- Stop running containers:
docker stop favorites
docker stop mongodb
docker container prune
- Create a custom network:
docker network create favorites-net
- Run MongoDB in the new network:
docker run -d --name mongodb --network favorites-net mongo
- Update Node.js connection string to use container name:
mongoose.connect(
'mongodb://mongodb:27017/swfavorites',
{ useNewUrlParser: true },
(err) => { /* callback */ }
);
- Build and run the Node.js container in the same network:
docker run --name favorites --network favorites-net -d --rm -p 3000:3000 favorites-node
Now, the app can communicate with MongoDB simply using the container name (mongodb
), no need to worry about IP addresses.
How Docker Handles This Behind the Scenes
When a container tries to connect using another container's name inside the same network, Docker automatically resolves the name to the right IP address.
No changes to your source code are necessary beyond using the correct container name.
Docker acts like a DNS resolver within the network.
Bonus: Docker Network Drivers
Docker supports different types of network drivers:
Driver | Behavior |
---|---|
bridge (default) | Standard network where containers can communicate if in the same network. |
host | Removes network isolation between container and host. |
overlay | Allows containers on different machines to communicate (requires Swarm mode). |
macvlan | Assigns a MAC address to a container for direct communication on the network. |
none | Disables networking completely. |
Third-party plugins | Add custom behavior and advanced networking features. |
To specify a driver while creating a network:
docker network create --driver bridge my-net
The bridge
driver is the most common and sufficient for most single-machine development and deployment scenarios.
Summary
We learned:
- Containers can communicate with the Internet easily.
- Containers cannot reach your local machine using
localhost
but can usehost.docker.internal
. - Container-to-container communication works via IPs but is better managed using a Docker network.
- Docker networks automatically resolve container names to IPs.
- Different network drivers offer different capabilities for your needs.
Mastering container networking is key to building production-ready distributed applications!