Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Default Docker network or your own: which to choose?

By updated

Improve this page

Logo Docker

A container started without any network option joins the default bridge network. It receives an IP address and can reach the outside thanks to an address translation, NAT. It reaches its neighbors by their address, but not by their name. A network you create yourself with docker network create changes two things. Container names resolve on it, and a container that is not part of it cannot reach it.

This page is for someone starting with Docker who wants an application made of two containers to find each other without writing an IP address into its configuration. It only covers the bridge network, on a single host (the machine that runs Docker). To publish a port to the outside, see publish a port. To cut a network off from the outside, see isolate a container from the network.

What you will learn

  • Read what the default bridge network gives a container: an address, a gateway, an exit through NAT
  • Check that a container name does not resolve on this network
  • Create a dedicated bridge network and attach containers to it with --network
  • Check name resolution, and the isolation of the containers left outside the network
  • Move a container with docker network connect and docker network disconnect
  • Read a network with docker network inspect
  • Tell the name of a Docker network from the name of the host interface, and link a gateway to its interface
  • Give a service a stable name with --network-alias, and check that this alias only holds on the network where it is set

Prerequisites

You need Docker installed (installing Docker), the basic commands run, exec, ps and rm (running a container) and the lesson publish a port, which explains what reaching a service from the host means. The commands are written for Docker Engine 29.9.2 and the alpine:3.24 image, whose busybox tools provide nc and ping.

What does the default bridge network give a container?

The default bridge network is a virtual switch that Docker creates at startup. It is called bridge, and the host sees it as an ordinary network interface. Every container that attaches to it receives an IP address taken from the network's range. The address of this interface serves as the containers' gateway, that is, their way out.

To reach the outside, a container's packets go through the host. The host applies a NAT rule (masquerade): it replaces the container's address with the host's, which lets the reply come back.

The diagram shows the default network on the left and a dedicated network on the right. Look first at who each container can address. On the default network, nothing translates names into addresses. The dedicated network embeds a small DNS service, a directory that translates a name into an IP address, and this service answers to the names of its members. Notice too that both networks leave through the same host NAT.

A Linux host carries two bridge networks. On the default network, three containers reach each other by IP address only. On the dedicated network vitrine, an embedded DNS service resolves the names of its two members. Both networks leave towards the outside through the host's NAT, and the docker network connect command links a container of the default network to the dedicated network.Linux hostOutsideDefault bridge networkUser-defined bridge network vitrineHost NAT(way out to the outside)pharelistens, name unknown to neighboursvigiereaches it by IP address onlypassantEmbedded DNSresolves the network's namescomptoirreached by namecaisseasks for comptoirdocker network connect
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

To see it, you start a listener and a second container that plays the client. The listener answers a fixed line to each connection on port 9100. It is an easy sign of life to read.

Terminal window
docker run -d --name phare alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a \
sh -c 'while true; do echo mot=phare | nc -l -p 9100; done'
docker run -d --name vigie alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sleep infinity
docker network inspect bridge --format 'sous-reseau={{(index .IPAM.Config 0).Subnet}} passerelle={{(index .IPAM.Config 0).Gateway}}'

The third command queries the network itself. The --format option keeps only two values: the subnet, the range of addresses the network hands out, and the containers' gateway. In the output, the first two lines are the identifiers of the containers just created. The last line gives, on this machine, the range 172.17.0.0/16 and the gateway 172.17.0.1.

0abdd4c4a657dec1846830189adb2ca30ccbb79334c631cfbd0f8cad1b241c6d
25ada90b5c783ac3d98402cd0ff0d5b8760f54540568681c451671ff7635e9f8
sous-reseau=172.17.0.0/16 passerelle=172.17.0.1

You then need the address of the phare container. Since Docker 29, the .NetworkSettings.IPAddress field no longer exists. The address is read under each network the container is attached to. The command below goes through all these networks, however many there are.

Terminal window
docker inspect phare --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.17.0.2

Here it is 172.17.0.2, just after the gateway. You will copy the address you get in place of ADRESSE.

Does the container really leave through NAT? Have vigie reach a public address. This command assumes the host has Internet access.

Terminal window
docker exec vigie ping -c 1 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
64 bytes from 1.1.1.1: seq=0 ttl=54 time=17.107 ms
--- 1.1.1.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 17.107/17.107/17.107 ms

The line 1 packets received, 0% packet loss shows that the reply came back. The container has no public address. The reply reaches it because the host rewrote the source address on the way out, as a home router does.

Why does a container name not resolve on this network?

The default bridge network has no name service. Docker does not register the container's name there. A client looking for phare therefore queries the host's DNS servers, which do not know it. These servers are the ones listed in the /etc/resolv.conf file, where a Linux machine keeps the address of the name server to query. To reach a neighbor, you therefore have to know its IP address.

Reach phare from vigie in two ways, first by its address, then by its name. Replace ADRESSE with the one you just noted.

Terminal window
docker exec vigie nc -w 3 ADRESSE 9100
docker exec vigie nc -w 3 phare 9100
echo "code de nc = $?"

The first command must print the line from phare. The second must fail, unable to translate the name, and the code printed will not be 0.

mot=phare
nc: bad address 'phare'
code de nc = 1

Writing an IP address into a configuration is fragile: it can change as soon as the container is recreated. A dedicated network fixes this flaw. The default network has another: all the containers started without options end up there together, and each one can reach all the others by their address.

How do you create a network of your own?

You create it with docker network create, then attach each container as soon as it is created with the --network option. Here, you also choose the subnet with --subnet, to decide the addressing plan yourself. Without this option, Docker takes a free range of its own choice, and that range may overlap a company network.

Terminal window
docker network create --subnet 10.77.0.0/24 vitrine
docker run -d --name comptoir --network vitrine alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a \
sh -c 'while true; do echo mot=comptoir | nc -l -p 9100; done'
docker run -d --name caisse --network vitrine alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sleep infinity

The first command must print the identifier of the new network, and the next two the identifier of each container.

c651b12896e871d5c57829d35fd40af801b71e15b27a5fab7cbde66421ce744a
065684b290179290df25f3fbffa469e24a5c21be748acefe64a4f2cc873fd5d6
6a768dcedfd7da0b3677826c4182a0a17b6a64b14782656998c766270c495b6a

A container started with --network vitrine is no longer on the default network. It is only on vitrine. docker network connect, presented below, acts differently: it adds a network and leaves the container on the previous ones.

On this network, name resolution works because Docker adds an embedded DNS. It is a small name server that listens at the address 127.0.0.11, inside each container of the network. It answers to the name given by --name. The first command below shows which name server the client uses. The second checks that the connection by name succeeds. The output is shortened: /etc/resolv.conf contains other lines, only the name server line is kept.

Terminal window
docker exec caisse cat /etc/resolv.conf
docker exec caisse nc -w 3 comptoir 9100
nameserver 127.0.0.11
mot=comptoir

Look at the nameserver 127.0.0.11 line. The client no longer queries the host's servers, it queries the network's embedded DNS, which knows comptoir. The last line, mot=comptoir, is the listener's reply. The connection by name succeeded.

No port was published with -p. Two containers on the same network do not need one: they talk to each other directly on the port where the application listens. Publishing only serves to open the service to the outside of the network, which the lesson on publish a port covers.

What happens to a container that is not in the network?

A container that is not in the network is isolated. It does not reach the members of the dedicated network by name, because the network's DNS does not know it. Nor does it reach them by address, because Docker does not link two bridge networks together. This is the second half of the point of a network of your own: a tool started by mistake on the default network no longer sees the application.

Start a container on the default network, then try to reach comptoir in two ways.

Terminal window
docker run -d --name passant alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sleep infinity
docker inspect comptoir --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

The first command prints the identifier of the new container, the second the address of comptoir on vitrine. Use this address in place of ADRESSE_COMPTOIR below.

15b2d5933b39cded13f33a4a02f5598e95946f64ddae6afd5a6c863c8562767b
10.77.0.2

Now try to reach comptoir by its name, then by its address.

Terminal window
docker exec passant nc -w 3 comptoir 9100
docker exec passant nc -w 3 ADRESSE_COMPTOIR 9100
echo "code de nc = $?"
nc: bad address 'comptoir'
code de nc = 1

Neither attempt returns the line from comptoir. The first prints bad address: the name is unknown. The second prints nothing: nc gives up after 3 seconds without having been able to connect to 10.77.0.2. The code 1 is that of this second command, the last one run.

Both routes are tested for a reason: if only the attempt by name failed, you would not know whether the name or the route is missing. Here, neither is available.

How do you move a container from one network to another?

docker network connect adds an existing container to a network, without restarting it. docker network disconnect removes it from it. There is no "move" command: a move is a connect followed by a disconnect. A container can belong to several networks at the same time, with one address per network.

Terminal window
docker network connect vitrine passant
docker exec passant nc -w 3 comptoir 9100
docker network disconnect vitrine passant
docker exec passant nc -w 3 comptoir 9100
echo "code de nc = $?"

The first nc, run between the two network commands, must receive the line from comptoir. The second, run after the removal, must fail as it did at the start.

mot=comptoir
nc: bad address 'comptoir'
code de nc = 1

The trap lies in what connect does not do: it does not remove the container from its current networks. For a container of the default network to move to vitrine, also disconnect it from the first with docker network disconnect bridge <container>. Otherwise it stays on both networks, so it is reachable from the default network, and the isolation you thought you had set up does not exist.

How do you read a network with docker network inspect?

docker network inspect displays a network as JSON: its type (the driver), its subnet, its gateway, and the list of attached containers with their address. It is the first reflex when two containers do not see each other, because it tells you in one command who is a member of what.

Terminal window
docker network inspect vitrine

The real output is about fifty lines long. It is shortened here to the two blocks that matter: IPAM.Config for the addressing plan and Containers for the members. The ... mark the omitted fields, and the container identifiers are truncated.

[
{
"Name": "vitrine",
"Driver": "bridge",
"IPAM": {
...
"Config": [
{
"Subnet": "10.77.0.0/24",
"Gateway": "10.77.0.1"
}
]
},
...
"Containers": {
"065684b29017...": {
"Name": "comptoir",
"IPv4Address": "10.77.0.2/24",
"IPv6Address": ""
},
"6a768dcedfd7...": {
"Name": "caisse",
"IPv4Address": "10.77.0.3/24",
"IPv6Address": ""
}
},
...
}
]

To keep only the essentials, pass --format a template, a small expression written in the Go language syntax, that goes through the list of members. It prints the name of each container and its address, which answers directly "who is on this network?".

Terminal window
docker network inspect vitrine --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
docker network ls
comptoir 10.77.0.2/24
caisse 10.77.0.3/24
NETWORK ID NAME DRIVER SCOPE
b564d4a88292 bridge bridge local
b5b096dacd08 host host local
262c750bdafd none null local
c651b12896e8 vitrine bridge local

docker network ls lists Docker's networks. You should find vitrine there, next to the three networks Docker creates on its own. The DRIVER column gives the type of network. bridge is the default network; host and none are two special cases this page does not use. To know which networks a container belongs to, see the NetworkSettings section in inspect a running container.

The name of a Docker network and the name of a host interface are two different things. vitrine and bridge are names for Docker; the host only knows interfaces, with their own name. To go from one to the other, start from the gateway: it is the address the network's interface carries on the host. A first command reads the gateway on the Docker side, a second finds it again on the host side.

For a network you create, Docker names the interface br- followed by the first 12 characters of the network identifier. It is the identifier printed by docker network create and repeated in the NETWORK ID column of docker network ls. Replace the identifier below with your own.

Run the second command on the Linux host, not in a container, since it is the host you are querying. The ip command lists the interfaces there, and the -br option summarizes each one on a single line.

Terminal window
docker network inspect vitrine --format '{{range .IPAM.Config}}{{.Gateway}}{{end}}'
ip -br addr show br-c651b12896e8
10.77.0.1
br-c651b12896e8 UP 10.77.0.1/24 fe80::a8b7:46ff:fe6c:575b/64

The first column of the second line is the interface name, the second its state, and the rest its addresses. 10.77.0.1/24 is indeed the gateway read by the first command.

This naming rule holds for the networks you create. Do not assume it for the default network. Read its gateway with the command given at the start of the page, then run ip -br addr, which lists all the host's interfaces, and look for the one whose addresses contain that gateway.

How do you give a service a stable name with a network alias?

A network alias is an additional name that a network's embedded DNS associates with a container, besides its name. It gives a service a stable name, independent of the container that provides it. The application aims at stock, and you can replace the entrepot container with another one without touching its configuration, as long as the new one carries the same alias.

You set the alias with --network-alias, at docker run time, next to --network.

Start entrepot with the alias stock, then ask caisse, which is on the same network, to resolve both names as an application would (getent hosts). The last docker inspect prints the aliases set by hand (Aliases) and all the names the network resolves for this container (DNSNames).

Terminal window
docker run -d --name entrepot --network vitrine --network-alias stock alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sleep infinity
docker exec caisse getent hosts entrepot
docker exec caisse getent hosts stock
docker inspect entrepot --format '{{json .NetworkSettings.Networks.vitrine.Aliases}} {{json .NetworkSettings.Networks.vitrine.DNSNames}}'

The container name and the alias return the same address. Aliases only contains stock. DNSNames adds the container name and a short identifier, which you can ignore.

3a3312da1e49eab623a431159f6986d78e48e747b7adff7f643a6ad5ee2709fd
10.77.0.4 entrepot entrepot
10.77.0.4 stock stock
["stock"] ["entrepot","stock","3a3312da1e49"]

An alias only resolves from the network where it is set. From passant, which left vitrine in the previous step and is only on the default network, the name stock does not resolve. The command prints nothing and returns the code 2.

Terminal window
docker exec passant getent hosts stock
echo "code de getent = $?"
code de getent = 2

Several containers can carry the same alias. The resolver then returns all their addresses, each on two lines (STREAM and DGRAM designate two kinds of connection, only the address matters here). A shared alias is a list of addresses, not a load balancer: it is up to the client to choose which one it uses.

Terminal window
docker run -d --name entrepot-bis --network vitrine --network-alias stock alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sleep infinity
docker exec caisse getent ahosts stock
b7dee95b3766034019afe5f4d3f17875dbd05e2fb2241f241e3662e5bddae36f
10.77.0.5 STREAM stock
10.77.0.5 DGRAM stock
10.77.0.4 STREAM stock
10.77.0.4 DGRAM stock

How do you clean up after the measurements?

A network can only be deleted once it has no attached container left. Delete the containers first, then the networks. The deletion is limited to the names on this page, so as not to touch your other containers. If you skipped the box on the second network, remove relais and annexe from these commands.

Terminal window
docker rm -f phare vigie comptoir caisse passant entrepot entrepot-bis relais
docker network rm vitrine annexe
docker network ls

The final list must no longer contain vitrine or annexe.

phare
vigie
comptoir
caisse
passant
entrepot
entrepot-bis
relais
vitrine
annexe
NETWORK ID NAME DRIVER SCOPE
b564d4a88292 bridge bridge local
b5b096dacd08 host host local
262c750bdafd none null local

Practice

The two labs in this lesson are played on a fresh virtual machine, with their own containers. The first validates what the default network gives and refuses: a container's address and gateway, the host interface that carries them, and a name that does not resolve while the connection by address succeeds. The second checks Docker's state once your commands have run: a network with the addressing plan requested, members that reach each other by name, and a container left outside that no longer reaches them.

Key points

The default bridge network gives every container an address, a gateway and an exit through NAT. It does not resolve names. You reach a neighbor there by its IP address, which changes when the container is recreated.

A network created with docker network create embeds a DNS that answers to container names, and a container cannot reach it until it is a member. You attach a container to it at creation with --network, and it is then no longer on the default network.

docker network connect adds a container to a network without removing it from the others, and docker network disconnect removes it from one. A container on two networks stays reachable from each of them. docker network inspect says who is a member of what, with which address.

The name of a Docker network is not the name of its interface on the host. For a network you create, the interface is called br- followed by the first 12 characters of the network identifier.

An alias set with --network-alias gives a service a stable name, independent of the container name. It only holds on the network where it is set.

Two containers on the same network talk to each other without publishing a port. A name that resolves does not prove that a connection succeeds: test by connecting.

Knowledge check

Check that the essentials of this guide stuck. The questions cover only what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn