Docker and Proxy

Working behind a corporate proxy can be a painful experience. Attempts to simply accessing web resources are spoiled.

When it comes to docker there are a couple configuration options we should know of. In this post I’d like to give an overview of their effects and what to keep in mind.

There are three different places where a proxy configuration can be applied:

  • docker client
  • docker daemon
  • container runtime

Docker client

Docker consists of a client and a daemon which don’t have to reside on the same host (see DOCKER_HOST env var). In case there is a proxy in between, you need to configure the docker client accordingly. Luckily, the client adheres to the standard of respecting the following environment variables:

http_proxy
https_proxy
no_proxy

Setting those on the machine of the client can be as easy as exporting the environment variables:

export http_proxy=http://proxy.example.com:80/
export https_proxy=https://proxy.example.com:443/
export no_proxy=dockerhost.local

Docker daemon

This will affect how the daemon itself connects to network resources.

The main use case is to be able to pull and push images from and to registries through a proxy — eg docker hub.

On the machine where the docker daemon is running we configure the proxy according to this page of the official documentation: https://docs.docker.com/config/daemon/systemd/#httphttps-proxy

Create a systemd drop-in directory for the docker service:
$ sudo mkdir -p /etc/systemd/system/docker.service.d 
Create a file called /etc/systemd/system/docker.service.d/http-proxy.conf that adds the HTTP_PROXY environment variable:
[Service] Environment="HTTP_PROXY=http://proxy.example.com:80/"
Or, if you are behind an HTTPS proxy server, create a file called /etc/systemd/system/docker.service.d/https-proxy.conf that adds the HTTPS_PROXY environment variable:
[Service]
Environment="HTTPS_PROXY=https://proxy.example.com:443/"
If you have internal Docker registries that you need to contact without proxying you can specify them via the NO_PROXY environment variable.
[Service]
Environment="HTTPS_PROXY=https://proxy.example.com:443/"
Environment="NO_PROXY=docker-registry.local"

Container runtime

Setting up this configuration was the most revealing for me.

It injects the proxy relevant environment variables into each container at runtime. Pretty neat is that it also affects the build time, as building each layer is effectively performed in a container. The injected environment variables are:

http_proxy
https_proxy
no_proxy
HTTP_PROXY
HTTPS_PROXY
NO_PROXY

This configuration is actually done on the machine of the docker client and in the home directory of the user executing the docker commands. We set it up according to the documentation here: https://docs.docker.com/network/proxy/

On the Docker client, create or edit the file ~/.docker/config.json in the home directory of the user which starts containers. Add JSON such as the following, substituting the type of proxy with httpsProxy or ftpProxy if necessary, and substituting the address and port of the proxy server. You can configure multiple proxy servers at the same time.
You can optionally exclude hosts or ranges from going through the proxy server by setting a noProxy key to one or more comma-separated IP addresses or hosts. Using the * character as a wildcard is supported, as shown in this example.
{
"proxies":
{
"default":
{
"httpProxy": "http://127.0.0.1:3001",
"httpsProxy": "http://127.0.0.1:3001",
"noProxy": "*.test.example.com,.example2.com"
}
}
}
Save the file.
When you create or start new containers, the environment variables are set automatically within the container.

So basically you can build any* regular Dockerfile behind a proxy and each layer is provided with the proxy information (eg RUN sudo apt-get install ... will respect it).

That means you will never have to do the following to your Dockerfile:

FROM my-base-image:1.0
ARG http_proxy
ARG https_proxy
ARG no_proxy
[...]

Having those ARGs in place will actually result in different images depending on where you build your image:

* not quite: executables which do not take those environment variables into consideration will fail. A famous example would be running a java process, eg gradle.


Conclusion

In most cases the proxy configuration for the docker daemon as well as the container runtime go hand in hand: When building images you probably need to access a docker registry as well as access web resources during the build time. 
The exception to this could be if we are solely using an internal registry but still require resources from behind the proxy — or vice versa.

Applying the configuration for the docker client is a rather special case in my experience. Also switching between different docker daemons (remote or local) can easily come in conflict with the container runtime proxy configuration.


TL;DR

docker proxy configurations overview