The symptom is: the host machine has proper network access, but programs running within containers can't resolve DNS names (which may appear to be "can't access the network" before investigating more).
$ sudo docker run -ti mmoy/ubuntu-netutils /bin/bash
root@082bd4ead733:/# ping www.example.com
... nothing happens (timeout) ... ^C
root@082bd4ead733:/# host www.example.com
... nothing happens (timeout) ... ^C
(The docker image mmoy/ubuntu-netutils is a simple image based on Ubuntu with ping
and host
included, convenient here since the network is broken and we can't apt install
these tools)
The issue comes from the fact that docker automatically configured Google's public DNS as DNS server within the container:
root@082bd4ead733:/# cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 8.8.8.8
nameserver 8.8.4.4
This just works in many configurations, but obviously doesn't when the host runs on a network where Google's public DNS are filtered by some firewall rules.
The reason this happened is:
- Docker first tries configuring the same DNS server(s) on the host and within the container.
- The host runs dnsmasq, a DNS caching service. dnsmasq acts as a proxy for DNS requests, hence the apparent DNS server in the host's
/etc/resolve.conf
isnameserver 127.0.1.1
, i.e. localhost. - The host's dnsmasq listens only for requests comming from localhost and blocks requests coming from the docker container.
- Since using
127.0.1.1
within docker doesn't work, docker falls back to Google's public DNS, which do not work either.
There may be several reasons why DNS is broken within docker containers. This question (and answers) covers the case where:
- dnsmasq is used. To check whether this is the case:
- Run
ps -e | grep dnsmasq
on the host. If the output is empty, you're not running dnsmasq. - Check the host's resolv.conf, it probably contains an entry like
nameserver 127.0.1.1
. If it containsnameserver 127.0.0.53
, you're probably runningsystemd-resolved
instead of dnsmasq. If so, you won't be able to use the solution forwading DNS requests to dnsmasq (the one usinglisten-address=172.17.0.1
). systemd-resolved hardcodes the fact that it listens only on the 'lo' interface hence there's no easy way to adapt this solution. Other answers below will work with systemd-resolved.
- Run
- Google's public DNS is filtered. Run
host www.example.com 8.8.8.8
. If it fails or times out, then you are in this situation.
What are the solutions to get a proper DNS configuration in this configuration?
Since the automatic DNS discovery is guilty here, you may override the default setting in docker's configuration.
First, get the IP of the DNS server dnsmasq is using with e.g.:
The IP addresses correspond to the
xx.yy.zz.tt
placeholders above.You can set the DNS at
docker run
time with the--dns
option:One advantage of this solution is that there is no configuration file involved, hence no risk of forgetting about the configuration and running into troubles later because of a specific config: you're getting this DNS configuration if and only if you type the
--dns
option.Alternatively you may set it permanently in Docker's configuration file,
/etc/docker/daemon.json
(create it, on the host, if it doesn't exist):You need to restart the docker daemon to take the
daemon.json
file into account:Then you can check the configuration:
Note that this hardcodes the DNS IP in your configuration files. This is strongly discouraged if your machine is a laptop that connects to different networks, and may be problematic if your internet service provider changes the IP of the DNS servers.
A clean solution is to configure docker+dnsmasq so than DNS requests from the docker container are forwarded to the dnsmasq daemon running on the host.
For that, you need to configure dnsmasq to listen to the network interface used by docker, by adding a file
/etc/NetworkManager/dnsmasq.d/docker-bridge.conf
:Then restart network manager to have the configuration file taken into account:
Once this is done, you can add
172.17.0.1
, i.e. the host's IP address from within docker, to the list of DNS servers. This can be done either using the command-line:... or through docker's configuration file
/etc/docker/daemon.json
(create it if it doesn't exist):(this will fall back to Google's public DNS if dnsmasq fails)
You need to restart docker to have the configuration file taken into account:
Then you can use docker as usual:
One way is to use a user defined network for your container. In that case the container's
/etc/resolv.conf
will have the nameserver127.0.0.11
(a.k.a. the Docker's embedded DNS server), which can forward DNS requests to the host's loopback address properly.If you use
docker-compose
, it will set up a custom network for your services automatically (with a file format v2+). Note, however, that whiledocker-compose
runs containers in a user-defined network, it still builds them in the default network. To use a custom network for builds you can specify thenetwork
parameter in the build configuration (requires file format v3.4+).A brutal and unsafe solution is to avoid containerization of the network, and use the same network on the host and on the container. This is unsafe because this gives access to all the network resources of the host to the container, but if you do not need this isolation this may be acceptable.
To do so, just add
--network host
to the command-line, e.g.Since
dnsmasq
is the issue, one option is to disable it on the host. This works, but will disable DNS caching for all applications running on the host, hence is a really bad idea if the host is used for applications other than docker.If you're sure you want to go this way, uninstall
dnsmasq
, e.g. on Debian-based systems like Ubuntu, runapt remove dnsmasq
.You may then check that
/etc/resolv.conf
within the container points to the DNS server used by the host.