Using the Add-Host Flag for DNS Mapping within Docker Containers

Written by: Ben Cane

In a previous article about the docker run command, one of my final thoughts was the following:

In this article, we covered quite a few options for the docker run command. However, while these options are key, they only scratch the surface of the available options to docker run. Some of the options are complex enough to deserve an article unto themselves.

In today's article, we are going to do just that: explore a single docker run command option. Specifically, we will explore an option that allows us to change DNS within a container.

Today, we will explore the --add-host flag.

What Does Add-Host Do?

The --add-host flag is used to add a single host to IP mapping within a Docker container. This flag can be useful when you need to connect a service within a container to an external host.

To get an understanding of how this works, let's use the --add-host flag to map testing.example.com to 10.0.0.1.

We will be testing with a container named curl. This container has the curl command preinstalled and set as the entrypoint. With this container, we can use the curl command to connect to our test host.

Before we start adding the --add-host flag, let's see what happens if we use this curl container to connect to the testing.example.com domain.

$ docker run --rm=True curl -I testing.example.com
curl: (6) Could not resolve host: testing.example.com

We can see from the above, the curl command returned an error Could not resolve host. This is the typical error we would see when trying to reach a domain that does not have a DNS entry.

This example shows us what happens without any --add-host flags added. Let's see what happens when we use this flag to map testing.example.com to 10.0.0.1.

$ docker run --rm=True --add-host=testing.example.com:10.0.0.1 curl -I testing.example.com
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 17 Nov 2017 14:01:20 GMT
Content-Type: text/html
Content-Length: 97652
Last-Modified: Sun, 16 Jul 2017 11:29:52 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: "596b4e30-17d74"
Accept-Ranges: bytes

Great! Now when our container reaches out to testing.example.com, it is able to connect to our example host. This means our internal DNS resolution works; but how exactly does it work?

Understanding How Add-Host Works

To understand how the --add-host flag works, let's spin up a simple Ubuntu container and take a look around. We can do this by using the -i (Interactive) and -t (Pseudo-TTY) flags.

These flags will start the container in a mode that allows us to interact with the running process. The process in this case will be /bin/bash; this gives us a shell within the container. With this shell, we can explore within the container itself.

$ docker run --rm=True --add-host=testing.example.com:10.0.0.1 -it ubuntu /bin/bash
root@a87b1beb2eee:/# 

With our shell startup successful, let's start looking at this container's configuration files. Specifically, let's look at the configurations that drive DNS resolution. The first file we will look at is the /etc/nsswitch.conf file.

The nsswitch.conf file in Unix and Linux is used to define where and how the system will perform name service-related lookups. The specific setting we are interested in is the hosts setting. The hosts setting is used to define the sources and order used for hostname resolution.

root@a87b1beb2eee:/# cat /etc/nsswitch.conf
# /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.
passwd:         compat
group:          compat
shadow:         compat
gshadow:        files
hosts:          files dns
networks:       files
protocols:      db files
services:       db files
ethers:         db files
rpc:            db files
netgroup:       nis

In the above, we can see that the hosts setting is set to files dns. This setting means that for hostname resolution, the system will first consult files, then dns.

When our system is performing a DNS lookup, it will first reference local configuration files, such as /etc/hosts. If the host is not found within /etc/hosts, the system will then query the name servers defined within the /etc/resolv.conf configuration file.

Something important to remember is that this methodology is the standard Linux setup. These steps are common for most Linux systems, not just containers.

What does this mean for the --add-host flag? Well, it means that there are only two places Docker could change DNS resolution: either the /etc/hosts file or the name servers referenced in /etc/resolv.conf.

The most likely candidate is the /etc/hosts file.

root@a87b1beb2eee:/# cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.0.0.1    testing.example.com
172.17.0.3      a87b1beb2eee

If we look at the /etc/hosts file, we can see an interesting entry.

10.0.0.1    testing.example.com

It appears that the --add-host flag places an entry into the /etc/hosts file. What is interesting about this is that editing the /etc/hosts file is a time-honored sysadmin trick. It has been used for many years to perform just this task: taking an unresolvable hostname and mapping it to a static IP.

!Sign up for a free Codeship Account

Using Add-Host to Override DNS Resolution

Another common use of the /etc/hosts file is overriding normal DNS. Since files is listed first within the /etc/nsswitch.conf configuration file, the /etc/hosts file is consulted before any traditional DNS name servers.

This means that it is possible to place an entry into the /etc/hosts file that overrides an existing name. To experiment a little, let's do this with Google's domain.

To start our experiment, lLet's take a look at what normally happens when we use our curl container to connect to google.com.

$ docker run --rm=True curl -I google.com
HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
Date: Fri, 17 Nov 2017 14:04:43 GMT
Expires: Sun, 17 Dec 2017 14:04:43 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 219
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN

From the above, we can see a pretty typical response from google.com. Now, let's see what happens if we use the --add-host flag to map google.com to 10.0.0.1.

$ docker run --rm=True --add-host=google.com:10.0.0.1 curl -I google.com
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 17 Nov 2017 14:05:52 GMT
Content-Type: text/html
Content-Length: 97652
Last-Modified: Sun, 16 Jul 2017 11:29:52 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: "596b4e30-17d74"
Accept-Ranges: bytes

From the above, we can see that our --add-host addition resulted in our curl command to connect to our example host. What this shows is that the --add-host flag can be used for more than mapping an unresolved hostname. It can be used to override traditional DNS as well.

This can be a useful method to force a container to connect to a specific host address.

Adding Multiple Entries

Another nice feature of this flag is that it can be specified multiple times.

$ docker run --rm=True --add-host=1.example.com:10.0.0.1 --add-host=2.example2.com:10.0.0.2 --add-host=3.example.com:10.0.0.3 ubuntu cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.0.0.1    1.example.com
10.0.0.2     2.example.com
10.0.0.3   3.example.com
172.17.0.3      17796a2cb640

In the above example, we can see that specifying the --add-host multiple times resulted in multiple entries within the /etc/hosts file. This is true whether multiple hosts are specified (as shown) or if the same host is specified.

This is useful in cases where you may want to specify multiple manual host to IP mappings.

Summary

In this article, we explored the --add-host flag available with the docker run command. We also learned that Docker uses a time-honored sysadmin trick for the heavy lifting behind the --add-host flag.

At this point though, you might be thinking, "This flag is interesting but how can it be used in the real world?"

When creating an application that uses another service, like Redis for example, I tend to have my application configured to reach out to the host of redis. I then link a Redis container with that name to my application container. This means my container's configuration is simple -- it just references redis. This works great when I can simply link the Redis container to my application container.

But what happens when the Redis instance can't be "linked" or is even outside of your control? We would then have to configure the application to reach out to a specific host, somewhat complicating the application's configuration.

The --add-host flag can be a simple way to keep a simplistic application configuration while still enabling the application to connect to the remote instance.

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.