Welcome back, aspiring Docker master! In our previous chapters, you’ve learned how to wrangle individual containers, build your own images, and even manage persistent data. That’s fantastic! You’re already doing more than just running simple commands.
But what happens when your application isn’t just one isolated container? What if you have a web server container, a database container, and an API container, all needing to talk to each other? How do they find each other? How do they communicate securely? And how do users outside your Docker host access your applications? This is where Docker networking comes into play, and it’s a fundamental skill for building real-world, multi-container applications.
In this chapter, we’re going to dive into the exciting world of Docker networking. We’ll learn how containers communicate, how to isolate them, and how to expose your services to the outside world. By the end, you’ll have a solid understanding of how to orchestrate conversations between your containers, setting you up perfectly for more advanced topics like Docker Compose. Get ready to connect the dots!
Core Concepts: How Containers Chat
Imagine your Docker host (your computer) as an apartment building. Each container is like an apartment within that building. For residents (containers) to communicate, they need a way to send messages. And for people outside the building (your host machine or the internet) to visit an apartment, there needs to be a clear path, like a doorbell and an address. Docker provides several networking options to manage these “conversations.”
The Default Bridge Network: Docker’s Built-in LAN
When you first run a container without specifying any network, Docker automatically connects it to a default network called bridge. Think of this bridge network as a small, private Local Area Network (LAN) inside your Docker host.
Containers connected to the same bridge network can communicate with each other using their IP addresses. They also get outbound access to the internet, just like devices on your home router.
Let’s quickly peek at the networks Docker manages:
docker network ls
You’ll usually see at least three networks:
NETWORK ID NAME DRIVER SCOPE
...
<some_id> bridge bridge local
<some_id> host host local
<some_id> none null local
...
The bridge network is the one we’re interested in for now. The host network essentially removes network isolation, making the container share the host’s network stack (use with caution!). none means no network connectivity at all.
You can inspect the bridge network to see its configuration, including the IP address range it uses:
docker network inspect bridge
This will output a lot of JSON, but look for the Subnet and Gateway under IPAM.Config. This gives you an idea of the IP range Docker assigns to containers on this network.
Why is this important? While the default bridge network is convenient, it has limitations, especially when you need containers to find each other by name (like webserver talking to database) rather than by constantly changing IP addresses.
User-Defined Bridge Networks: Your Custom LANs
This is where the real power of Docker networking begins! User-defined bridge networks are the recommended way to connect containers within your applications. They offer several key advantages over the default bridge network:
- Automatic DNS Resolution: Containers connected to a user-defined bridge network can resolve each other by their container names or service names. No more remembering IP addresses! This is a game-changer for multi-container applications.
- Isolation: User-defined networks provide better isolation. Containers on one user-defined network cannot communicate with containers on another user-defined network by default, unless you explicitly connect them.
- Configurability: You can configure these networks with specific IP subnets if needed (though Docker usually handles this perfectly well for you).
- Easier Cleanup: When you remove a user-defined network, all associated network rules are cleaned up automatically.
Think of user-defined networks as creating separate, named LANs for different parts of your application. Your frontend-app-network can host your web servers, and your backend-db-network can host your databases.
Port Mapping (-p or --publish): Opening Doors to Your Apartments
Even if your containers can talk to each other internally, how do you, as the user on your host machine, access a web server running inside a container? This is where port mapping comes in.
When you run a command like docker run -p 8080:80 nginx, you’re telling Docker: “Take traffic coming into port 8080 on my host machine and forward it to port 80 inside the nginx container.”
- The first number (
8080) is the host port. - The second number (
80) is the container port.
This is how you expose services running inside your containers to the outside world (your host machine or even the internet, if your host is publicly accessible).
Step-by-Step Implementation: Building a Connected App
Let’s get our hands dirty and put these concepts into practice! We’ll create a simple scenario with two Nginx containers. One will act as a “frontend” (accessible from your host), and the other as a “backend” (only accessible by the frontend container).
Step 1: Cleaning Up (Good Practice!)
Before we start, let’s make sure we don’t have any old containers running that might interfere.
docker rm -f $(docker ps -aq) # Removes all stopped and running containers
This command uses docker ps -aq to list all container IDs (active and quiet) and then forces their removal. It’s a handy cleanup command!
Step 2: Create a User-Defined Bridge Network
First, we’ll create our custom network. Let’s call it my-web-app-network.
docker network create my-web-app-network
You should see a message like e6a...c20 created (your ID will be different).
Now, verify it’s there:
docker network ls
You should now see my-web-app-network listed among your networks. Awesome! You’ve just built your first custom container LAN.
Step 3: Run Our “Backend” Container
Let’s start with a backend Nginx container. This container will not be directly accessible from your host machine. It will only be reachable by other containers on my-web-app-network.
docker run -d --name web-backend --network my-web-app-network nginx
Let’s break down this command:
docker run: The command to run a new container.-d: Runs the container in detached mode (in the background).--name web-backend: Gives our container a memorable name. This is crucial for DNS resolution on user-defined networks!--network my-web-app-network: This is the magic! It connects ourweb-backendcontainer to the network we just created.nginx: The image we want to use.
Check if it’s running:
docker ps
You should see web-backend listed. Notice there’s no port mapping for this one.
Step 4: Run Our “Frontend” Container with Port Mapping
Now, let’s run our frontend Nginx container. This one needs to be accessible from your host machine, and it also needs to communicate with web-backend.
docker run -d --name web-frontend --network my-web-app-network -p 8080:80 nginx
Let’s dissect this command:
docker run -d --name web-frontend: Similar to the backend, running detached with a name.--network my-web-app-network: Again, connecting it to our custom network so it can talk toweb-backend.-p 8080:80: This is the port mapping! It maps port8080on your host to port80inside theweb-frontendcontainer. This allows you to access Nginx (which listens on port 80 by default) from your browser atlocalhost:8080.nginx: The image.
Verify both are running:
docker ps
You should now see both web-backend and web-frontend running, both on the my-web-app-network.
Step 5: Test Host-to-Container Communication
Open your web browser and navigate to http://localhost:8080.
You should see the familiar “Welcome to Nginx!” page. Congratulations! You’ve successfully exposed a service running inside a Docker container to your host machine.
Step 6: Test Container-to-Container Communication by Name
Now for the really cool part: let’s see if web-frontend can talk to web-backend using its name.
First, we need to get a shell inside the web-frontend container:
docker exec -it web-frontend bash
You’re now inside the web-frontend container. It’s like you’ve entered apartment “web-frontend” in our building.
Now, let’s try to ping the web-backend container by its name:
ping web-backend
You should see a successful response!
PING web-backend (172.xx.x.x) 56(84) bytes of data.
64 bytes from web-backend.my-web-app-network (172.xx.x.x): icmp_seq=1 ttl=64 time=0.0xx ms
...
(Press Ctrl+C to stop the ping.)
This is powerful! Docker’s built-in DNS resolver for user-defined networks automatically translated web-backend into its correct IP address.
Let’s take it a step further. Nginx serves a default HTML page. Let’s try to curl (make an HTTP request) to web-backend from web-frontend:
curl web-backend
You should see the HTML content of the default Nginx page served by web-backend! This confirms that web-frontend can successfully make HTTP requests to web-backend using its container name.
To exit the container’s shell, just type exit:
exit
You’re back on your host machine.
Mini-Challenge: Network Isolation and Connection
Let’s solidify your understanding of network isolation and how to connect containers.
Challenge:
- Stop and remove all running containers and networks from our previous exercise.
- Create two separate user-defined networks:
dev-networkandprod-network. - Run an
nginxcontainer nameddev-webon thedev-network. - Run a
rediscontainer namedprod-cacheon theprod-network. - From inside the
dev-webcontainer, try toping prod-cache. What happens? (You might need toapt update && apt install iputils-pinginsidedev-webfirst). - Connect the
dev-webcontainer to theprod-networkwithout stopping it. - Now, from inside
dev-web, try toping prod-cacheagain. What happens now?
Hint:
- To connect an already running container to a network, use
docker network connect <network_name> <container_name>. - Remember to
docker exec -it <container_name> bashto get inside the container.
What to Observe/Learn: You should observe that containers on completely separate networks cannot communicate by name (or IP, easily). Only when they share a common network can they resolve each other’s names and communicate. This highlights the power of network isolation for organizing your applications.
Common Pitfalls & Troubleshooting
“My containers can’t talk to each other by name!”
- Check: Are both containers on the same user-defined network? Use
docker inspect <container_name>for each container and look under theNetworkssection. If they are on thebridgenetwork, they won’t resolve by name. - Solution: Create a user-defined network and connect both containers to it using
--network <network_name>when running them, ordocker network connect <network_name> <container_name>for existing containers.
- Check: Are both containers on the same user-defined network? Use
“I can’t access my container from my host machine!”
- Check: Did you correctly map the ports? Use
docker psand look at thePORTScolumn. Is0.0.0.0:HOST_PORT->CONTAINER_PORT/tcplisted correctly? - Check: Is the application inside the container actually listening on the
CONTAINER_PORTyou specified? (e.g., Nginx listens on 80, Apache on 80, some apps on 3000, 5000, etc.). - Check: Is your host machine’s firewall blocking the
HOST_PORT? This is less common locally but can happen on cloud servers.
- Check: Did you correctly map the ports? Use
“My container can’t reach the internet!”
- Check: Is it connected to any network? If it’s on
none, it has no network connectivity. - Check: If you’re using a very custom network setup, ensure it has a gateway that routes to the internet. For default and user-defined bridge networks, this usually works out of the box.
- Check: Is it connected to any network? If it’s on
Summary
Phew! You’ve had a great conversation about container conversations! Let’s recap the key takeaways from this chapter:
- Docker Networking is Essential: It’s how containers communicate with each other and the outside world.
- Default
bridgeNetwork: Containers run on this by default, allowing IP-based communication and outbound internet access, but no automatic DNS resolution by name. - User-Defined Bridge Networks: These are the recommended way to network your applications. They provide:
- Automatic DNS resolution by container name.
- Better isolation between different application components.
- Easier management.
- Port Mapping (
-p HOST_PORT:CONTAINER_PORT): This is how you expose a service running inside a container to your host machine so you can access it vialocalhost:HOST_PORT. docker networkCommands: You learneddocker network lsto list networks,docker network inspectto view details,docker network createto make new networks, anddocker network connectto attach running containers to networks.
You’ve now mastered the art of making your containers talk to each other and interact with the host. This knowledge is absolutely crucial as you move towards building more complex, multi-service applications.
What’s Next?
Connecting individual containers with docker run --network is powerful, but it can get cumbersome for many services. In our next chapter, we’ll introduce Docker Compose, a tool that allows you to define and run multi-container Docker applications with a single command, leveraging all the networking concepts you just learned! Get ready to orchestrate your entire application stack!