A basic guide to networking in docker - host networks, bridge networks and macvlan networks

Docker Networks

Docker Aug 23, 2021

Networking in docker can be confusing. Containers can connect to one or more docker networks (not to be confused with your home LAN network) at any given time. As in the cover image for this post, the IP address of your docker (bridge) networks will start with 172. Traditionally, the default bridge network which is first created when you install docker will be 172.17.0.0/16, however you can create any other subnet within the 172.x.0.0/xx range you like for your own, custom bridge networks.

There are three main types of docker network you will encounter:

The Host Network

As the name suggests, the Host network uses exactly the same IP as the host machine. This is used when the container should (or needs to) be on the same IP address as the host, or needs some settings only available through the host network. This however comes with limitations such as the inability to specify custom port mapping for your container, which can be important if certain host-machine ports are already in use.

Example: I have a container which uses ports I know are not already in use (e.g. port 3000). For simplicity, I can specify the container to use the host network without the need to publish ports in docker-compose or docker run, and the container will be available at hostIP:3000
💡
Containers which are open to the internet could be exploited by hackers. By default and unless otherwise specified, a container would be on its own newly-created network, meaning should a hacker gain access, they will be constrained by the bridge network and will not have access to your machine's network. If you use the host network, you lose this layer of security

The Bridge Network (click to jump to the how-to section on this page)

Probably the most commonly used. As already mentioned, a default bridge network is created when you install docker. However when using docker-compose, if you create a container without specifying a network, docker will automatically create a new bridge network for your container. This creates a network with an IP in the 172.x.x.x range, and assigns your container IP(s) automatically (unless specified in your container creation process).

Some people are content putting all their containers on the one, default bridge network. Sounds a bit like the one about putting all your eggs in one basket doesn't it? For security, it's recommended that you only use the same network for containers which require it.

Example: I am using Bitwarden as my password manager. There is lots of sensitive data on it, and it has a higher level of built-in security than say Homer. If someone manages to hack into Homer and it is on the same bridge network as my Bitwarden instance, it immediately becomes a lot easier for the hacker to attack Bitwarden. Hence, I keep Bitwarden on its own bridge network
💡
Edit: The default bridge network (i.e. the network your container uses when you use the network_mode: bridge variable) is now a legacy hold-over, and is generally not recommended for use.

The Macvlan (click to jump to the how-to section on this page)

This type of network must be created by the user (either manually via SSH or as part of a docker-compose.yml setup) and can technically create a network on any private IP subnet, including that of your host's LAN.

Example: if your host is the only machine on a particular subnet and has an IP of 192.168.1.2, the macvlan can be on the same 192.168.1.0/24 subnet and use any unused IP from 192.168.1.3 upwards to 1.254.

This also comes with limitations, mainly that the macvlan network cannot by default communicate with the host machine. If a container requires a macvlan and communication with the host machine, then it must also connect to a second network which will be a bridge network.

Example: I want to use an adblocker such as Adguard or Pihole. I want my NAS to be able to make use of this adblocker, and I want the adblocker to act as my DHCP server.
--
If I use a host or bridge network for this container, the IP used to access the container will be the same as my NAS, and would cause a whole lot of issues with routing.
--
I want the container to be on its own, separate IP. For this reason, I create a macvlan network.
--
But why would I also need it to communicate with the host? Well, Adguard can be secured using SSL, however in some setups/cases it will use a domain certificate requested through and provided by the host. This means it also needs to communicate with the host machine. As the macvlan does not have any connection to the host machine, you must also connect it to a bridge network so that adguard can make this connection.

Network Creation

Again there are a number of ways you can do this:

  • with the Synology Docker GUI
  • with Portainer (a container itself and overall extremely helpful)
  • with SSH

I prefer using SSH. I've had bad experiences with Portainer in the past creating (and then not being able to delete) invalid networks. I flat out don't like the docker GUI. So I now solely use the SSH method.

Bridge Network Creation

  1. Log in to your SSH session
  2. Input the following:
docker network create --subnet=172.xx.0.0/24 --gateway=172.xx.0.1 [networknamegoeshere]

For instance, I want to create a network called 'NetMcNetFace' on the 172.50.0.0 network:

docker network create --subnet=172.50.0.0/24 --gateway=172.50.0.1 NetMcNetFace
creating a bridge network
the /24 after the IP subnet specifies how many IP addresses are available, and is called the CIDR. /24 allows you to use all the way from 172.50.0.0 through to 172.50.0.254. The higher the CIDR number, the fewer the IPs allowed - for instance 172.50.0.0/29 would allow you to use 172.50.0.0 through to 172.50.0.5. More info can be found here
Remember that any networks you set up which require internet access, and which are NOT on the same subnet as your NAS/machine will need to be unblocked in your machine's firewall settings
💡
For more in-depth options on network creation with docker-compose, click here

MacVlan Network Creation

To create a macvlan network it's a little different, we need to specify the driver (-d) and parent (when creating a bridge network, as no driver is specified, it defaults to 'bridge'). From ssh:

docker network create -d macvlan --subnet=xxx.xxx.0.0/24 --gateway=xxx.xxx.0.1 -o parent=[yourHostNetworkAdapter] [networknamegoeshere]

You'll notice there's a bit more going on towards the end here too - you need to specify a parent network. On linux in SSH, this can be achieved by typing

ifconfig

into your SSH session, which will return a bunch of information looking a bit like this:

the output of typing ifconfig

What we're searching for is the IP address of your NAS or host machine - normally 192.168.1.x or 192.168.0.x. Mine is 192.168.1.2 which corresponds to the ovs_eth1 network:

identifying our network which corresponds to the IP of the host machine on our LAN

We then use this as our parent network. So if I wanted a container which was accessible from the same subnet as my router (192.168.1.0), I create a macvlan network called MyMacVlan at 192.168.1.0:

docker network create -d macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 -o parent=ovs_eth1 MyMacVlan
remember, all commands and information typed into the cli are case sensitive

DSM 7 update:

After upgrading my NAS to DSM 7, I found that the Open vSwitch settings needed for Virtual Machine Manager (which I use for Home Assistant) were conflicting with the macvlan network, and stopping all connectivity to the NAS on that network interface (my LAN 2). The error in question read network dm-1234567890 is already using parent interface ovs_eth1. I cannot for the life of me identify it this network, nor remove it with a docker network prune command.

I raised this with Synology, however they're saying that macvlan networks are not supported on Synology Diskstations (even though it's available through the package they publish) and there's nothing they can do to help.

It also looks like this issue has been around for some people for a while,


Specifying a network in docker-compose

As previously shown you can specify a network for a particular container or group of containers to join. To do this, you must specify the networks in the first column, and then specify the network to join in the service:

##############NETWORKS##############
networks:
  MyMacVlan:
    external: true
  AnotherBridgeNetwork:
  	external: true
##############NETWORKS##############

services:
  radarr: #movie search and organizing agent
    image: ghcr.io/linuxserver/radarr
    container_name: radarr
    networks:
    	- MyMacVlan
specifying networks for the container to join

You'll notice that although we specified two networks at the top of our docker-compose file (MyMacVlan  and AnotherBridgeNetwork) we only connected our radarr service to MyMacVlan. This doesn't break anything, but you will receive a notification in your terminal window after the compose has run that a network was specified but not used.

We can go one step further and specify an IP address for our container to join. This means instead of it being randomly assigned every time the container is recreated, it will always have the same IP. When we're sharing the same subnet as the other devices on our router, or we need it to always be accessible at the same IP, this is important.

##############NETWORKS##############
networks:
  MyMacVlan:
    external: true
  AnotherBridgeNetwork:
  	external: true
##############NETWORKS##############

services:
  radarr: #movie search and organizing agent
    image: ghcr.io/linuxserver/radarr
    container_name: radarr
    networks:
    	MyMacVlan:
        	ipv4_address: 192.168.1.100

This can be done with either macvlan or bridge networks. In general it is not recommended to assign specific container IPs unless there is a very good reason to, as in some cases it can break the set up.

Finally and alternatively, you could even create the macvlan network in the docker-compose file. If we use the same parameters as the one we created above, it would look something like this:

networks:
  MyMacVlan:
    name: MyMacVlan
    driver: MyMacVlan
    driver_opts:
  	  parent: ovs_eth1
    ipam:
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1

The benefit of adding it like this to your docker-compose is that you don't need to create (or then destroy) the network separately to creating the container, as your compose file will do it all for you.


Removing docker networks

In most cases it's easy to remove a docker network. It's even easier with Portainer, but if you don't use or have that:

Make sure that no containers are using the network

You can do this by inspecting a network, for instance docker network inspect newnetwork returns:

[
    {
        "Name": "newnetwork",
        "Id": "db31a0503c6c932dbfa359e1f8c7722d8025a1bfa67a3993c1d311c1c221b4c8",
        "Created": "2021-09-01T21:57:33.095320584+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.31.0.0/16",
                    "Gateway": "172.31.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]
newnetwork is the name of my network, replace it with your own network's name

Under "Containers" you can see there's nothing listed. If there were, you would see something like this:

[
    {
        "Name": "newnetwork",
        "Id": "db31a0503c6c932dbfa359e1f8c7722d8025a1bfa67a3993c1d311c1c221b4c8",
        "Created": "2021-09-01T21:57:33.095320584+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.31.0.0/16",
                    "Gateway": "172.31.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "fbfeacf9e6219f85a4cb09a196be27292c8600d830c659f2b8c1ddc069a07463": {
                "Name": "CFddns",
                "EndpointID": "2b6f293e975775d5ddfcc808a9de97519fb80da59bd2ec1338c99a8ff34cc50c",
                "MacAddress": "02:42:ac:1f:00:02",
                "IPv4Address": "172.31.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
you would see an entry for each container connected to the network

Note how you can now see the container ID, name, endpoint etc.

If you do have a container listed, then typing docker network disconnect [network name] [container name] will do it, so:

docker network disconnect newnetwork CFddns

removes the 'CFddns' container from the 'newnetwork' container.

note that in SSH you won't get any confirmation that the command has been successful

Removing/deleting a docker network

Now that your network is no longer being used, you can remove it using the following:

docker network rm [docker network name]

If you run this command when a container is still connected to the network in question, you'll get an error back, but it shouldn't cause any harm to your system.

Removing all unused networks in one go

To do this, you can use the following command:

docker network prune

This is a handy tool for simply removing everything you no longer need, and can be applied to volumes ( docker volume prune) images etc.


Advanced Docker Networking using Compose
A more comprehensive walkthrough of networking examples with docker compose. What I wish I’d known sooner rather than later
Getting the most out of docker-compose: tips and tricks
A list of handy tips you can implement immediately when creating your docker compose files

PTS

PTS fell down the selfhosted rabbit hole after buying his first NAS in October 2020, only intending to use it as a Plex server. Find him on the Synology discord channel https://discord.gg/vgSq5pcT

Have some feedback or something to add? Comments are welcome!

Please note comments should be respectful, and may be moderated