Docker MTU issues and solutions
If you want to use Docker on servers or virtual machines, technical limitations can sometimes lead to a situation in which – even without intentional limitation – it is not possible to access the outer world from a docker container.
Docker MTU configuration
A common problem when operating dockers within a virtualization infrastructure is that the network cards provided to virtual machines do not have the default MTU of 1500. This is often the case, for example, when working in a cloud infrastructure (e.g. OpenStack). The Docker Daemon does not check the MTU of the outgoing connection at startup. Therefore, the value of the Docker MTU is set to 1500.
Detecting the problem
With the command ip link you can display the locally configured network cards and their MTU:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1454 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default link/ether uu:vv:ww:xx:yy:zz brd ff:ff:ff:ff:ff:ff
If the outgoing interface (in this case ens3) has an MTU smaller than 1500, some action is required. If it is greater than or equal to 1500, this problem does not apply to you.
Solving the problem (docker daemon)
To solve the problem, you need to configure the Docker daemon in such a way that the virtual network card of newly created containers gets an MTU that is smaller than or equal to that of the outgoing network card. For this purpose create the file /etc/docker/daemon.json with the following content:
{ "mtu": 1454 }
In this example, I chose 1454 as the value, as this corresponds to the value of the outgoing network card (ens3). After restarting the Docker daemon, the MTU of new containers should be adapted accordingly. However, docker-compose create a new (bridge) network for every docker-compose environment by default.
Solving the problem (docker-compose)
If you work with docker-compose, you will notice that in containers created by docker-compose, the MTU of the daemon is not inherited. This happens because the mtu entry in /etc/docker/daemon.json file only affects the default bridge. Therefore you have to specify the MTU explicitly in the docker-compose.yml for the newly created network:
... networks: default: driver: bridge driver_opts: com.docker.network.driver.mtu: 1454
After rebuilding the docker-compose environment (docker-compose down; docker-compose up), the containers should use the modified MTU.
I personally don’t like this solution, because the docker-compose files have to be specially adapted to their environment and therefore lose their portability. Unfortunately, I am not aware of any other solution to this problem at the moment.
3 COMMENTS
Thanks for this, solved my problem. I spent a couple of days trying to work this out. I’m running docker on a single Ubuntu host, but behind a VPN which is also configured on the host. The containers could ping out and resolve DNS fine, but couldn’t transfer files. Turns out the VPN connection was using a MTU of 1400, and docker was using the default of 1500.
Is there a Kubernetes/Calico setting that sets MTU’s for new containers like your docker-compose example? I updated Calico MTU as follows, to no effect: kubectl edit configmaps calico-config -n kube-system
(Red Hat Enterprise Linux. /etc/docker/daemon.json value is just getting ignored. docker0 network still shows default 1500)
Hi,
I guess all the calico nodes need to be restarted. I usually use Kubespray for installing a Kubernetes clustere, which has also support for setting a calico MTU, so maybe looking at the code there can help you to understand how MTU can be configured. Maybe you can check https://mlohr.com/kubernetes-cluster-on-hetzner-bare-metal-servers/ for more information.
Best regards
Matthias