← All blogs
  • DevOps
  • Docker
  • Containers
  • Docker Networking

Docker Networking Explained: Why Do We Need a Custom Network?

A practical explanation of Docker networking, container isolation, service discovery, and why custom networks are useful.

Originally published on Medium ↗. Read the full article below.

When I first started learning Docker, I came across this command:

docker network create bank-network

And honestly, my first question was:

“Why are we creating a network? Doesn’t Docker already have a bridge network?"

Docker already gives us a default network called bridge, so creating another one can feel unnecessary.

But once you understand how containers communicate with each other, the reason becomes pretty simple.

Let’s break it down from the beginning.

Docker Networking Explained: Why Do We Need a Custom Network?

First: What Is a Docker Container?

A container is basically a small, isolated environment where your application runs.

For example, imagine we’re building a simple application.

We might have:

┌──────────────┐
│     App      │
└──────────────┘

Our application might need a database.

So we create another container:

┌──────────────┐       ┌──────────────┐
│     App      │ ────> │  PostgreSQL  │
└──────────────┘       └──────────────┘

Now we have a question:

How does the App container find the PostgreSQL container?

That’s where Docker networking comes in.

Containers Need a Way to Talk

Think about your normal computer.

If one application wants to communicate with another application, there needs to be some way for them to communicate.

The same thing happens with Docker containers.

For example:

App
 │
 │ "Hey PostgreSQL, give me the data."
 │
 ▼
PostgreSQL

Docker provides networks so containers can communicate with each other.

Docker Already Gives Us a bridge Network

If you run:

docker network ls

you’ll see something similar to:

NETWORK ID     NAME      DRIVER
xxxxxx         bridge    bridge
xxxxxx         host      host
xxxxxx         none      null

The bridge network is created automatically by Docker.

So you might think:

“Great. I’ll just put all my containers on**bridge."

You can.

But when you’re building an application with multiple containers, there is a better option: a user-defined bridge network.

Create Your Own Network

We can create a custom network:

docker network create bank-network

Now Docker has a network called:

bank-network

We can put our containers on it.

For example:

docker run -d \
  --name postgres \
  --network bank-network \
  postgres

And our application:

docker run -d \
  --name app \
  --network bank-network \
  my-app

Now we have:

bank-network
          ┌──────────────────┐
          │                  │
          │   App            │
          │    │             │
          │    │             │
          │    ▼             │
          │  PostgreSQL      │
          │                  │
          └──────────────────┘

Both containers are on the same network, so they can communicate with each other.

But here’s where things get interesting.

Don’t Depend on Container IP Addresses

Suppose our PostgreSQL container gets an IP address like:

172.18.0.2

Our application could technically connect to:

172.18.0.2

But we don’t want to do that.

Why?

Because container IP addresses can change.

Imagine This Happens

Today:

postgres → 172.18.0.2

Tomorrow, you delete the PostgreSQL container and create it again.

Docker might give it:

postgres → 172.18.0.5

Your application was using:

172.18.0.2

So now the application can’t find the database.

That’s a problem.

Use the Container Name Instead

Instead of using the IP address, we can use the container name.

Our PostgreSQL container is called:

postgres

So our application can connect to:

postgres:5432

For example:

postgres://root:secret@postgres:5432/mydb

Look at this part:

@postgres:5432

We’re saying:

“Connect to the container called postgres on port**5432."

Docker takes care of finding the current IP address.

So it doesn’t matter if PostgreSQL is:

172.18.0.2

or:

172.18.0.5

or:

172.18.0.10

Our application still uses:

postgres

That’s much easier to manage.

Think of It Like Your Contacts App

Here’s an easy way to think about it.

Imagine your friend’s phone number is:

9876543210

You could memorize the number.

But what happens if they change their phone number?

You’d have to update everything.

Instead, you save them as:

Rahul

Your phone keeps track of the number.

Docker networking works in a similar way.

Instead of your application remembering:

172.18.0.5

it remembers:

postgres

Docker figures out the IP address.

So Why Create a Custom Network?

Now we can answer the original question.

We create a custom network because it gives our containers a clean way to find and communicate with each other.

For example:

bank-network
│
├── app
├── postgres
└── redis

The app can talk to:

postgres

and:

redis

without worrying about their IP addresses.

What About the Default bridge?

This is important.

The default bridge network isn't "bad."

You can absolutely use it.

It’s useful for simple experiments and temporary containers.

The important difference is that user-defined bridge networks provide automatic DNS-based name resolution between containers, along with better control over the network.

That makes them much more convenient when you’re building an application with multiple containers.

Another Benefit: Keeping Things Separate

Imagine you have two applications.

Application 1

bank-network
app
postgres
redis

Application 2

shop-network
app
postgres
redis

They’re on separate networks.

This helps keep the applications isolated from each other.

You can think of it as giving each application its own private network:

Bank Application
├── App
├── Database
└── Redis
Shop Application
├── App
├── Database
└── Redis

As your infrastructure grows, this kind of separation becomes increasingly useful.

One More Thing: localhost Can Be Confusing

This is one of the most common Docker mistakes beginners make.

Suppose your application is running inside a container.

You might think:

localhost:5432

means:

“Connect to my PostgreSQL container.”

But that’s not what it means.

Inside a container:

localhost

means:

“This container.”

So if your application is running inside:

app

then:

localhost

refers to the app container itself.

It does not refer to:

postgres

Instead, your application should use the PostgreSQL container’s name:

postgres:5432

So:

localhost

and:

postgres

are two very different things in Docker.

What About -p 5432:5432?

You may have also seen:

docker run -p 5432:5432 postgres

This is a different concept.

It allows your computer to access the PostgreSQL container.

Think of it like:

Your Computer
     │
     │ localhost:5432
     ▼
PostgreSQL Container

For example, a database client running on your computer could connect using:

localhost:5432

But another container on the same Docker network can communicate using:

postgres:5432

So:

From your computer:
localhost:5432

while:

From another Docker container:
postgres:5432

This distinction is important when you’re starting with Docker.

You Don’t Need to Expose Every Port

Here’s another useful Docker concept.

Container-to-container communication doesn’t require publishing every port to your host.

Suppose you have:

API → PostgreSQL

PostgreSQL might only need to be accessible by the API.

You don’t necessarily need to publish PostgreSQL’s port to your host.

The Docker network can provide the internal communication path.

For example:

Docker
        ┌─────────────────────┐
        │                     │
        │  API ──────── DB     │
        │                     │
        └─────────────────────┘
                   │
                   │
             Published ports
                   │
                   ▼
                 Host

Only services that need to be accessed from outside Docker generally need published ports.

What Happens When a Container Is Recreated?

This is where the custom network becomes particularly useful.

Imagine:

postgres
   ↓
172.18.0.2

You remove the container.

Docker creates a new one:

postgres
   ↓
172.18.0.7

Your application doesn’t need to change.

It still connects to:

postgres:5432

Docker resolves the name to the current container address.

That’s the abstraction we want.

The Real Mindset Shift

The biggest lesson here isn’t really about Docker commands.

It’s about how applications find the services they depend on.

Don’t make your application think:

“My database lives at 172.18.0.7.”

Make it think:

“My database is called postgres."

The infrastructure can change without forcing you to change your application configuration.

That’s a useful concept far beyond Docker.

When Should You Create a Custom Network?

A simple rule:

One-off containers

The default network may be perfectly fine.

Multiple containers forming an application

Consider creating a user-defined network.

For example:

docker network create app-network

Then:

app-network
│
├── frontend
├── backend
├── postgres
├── redis
└── worker

Your services can communicate using meaningful names rather than container IP addresses.

A Practical Example

Create the network:

docker network create app-network

Run the database:

docker run -d \
  --name postgres \
  --network app-network \
  postgres

Run your application:

docker run -d \
  --name app \
  --network app-network \
  my-app

Now the application can communicate with PostgreSQL using:

postgres:5432

No need to find the database container’s IP address.

The Main Idea

Docker networking might look complicated when you first encounter it.

But the core idea is simple:

Containers need a way to communicate with each other.

A user-defined network gives those containers a dedicated network and makes it easy for them to discover each other by name.

So instead of thinking:

"What's PostgreSQL's IP address?"

think:

"What's PostgreSQL's name?"

And let Docker handle the rest.

Final Takeaway

Docker Networking Explained: Why Do We Need a Custom Network?

You don’t create a custom Docker network because the default bridge network doesn't work.

You create one because it provides a cleaner setup for applications made up of multiple containers.

Remember these three things:

1. Containers need networks to communicate.

2. Don’t depend on container IP addresses.

3. Use container names on a user-defined network.

So instead of:

App → 172.18.0.5

think:

App → postgres

Let Docker figure out the IP.

Once you understand this, Docker networking becomes much easier to understand.