Are you working with Docker in a corporate network, on a server with limited access, or do you want your containers to access the internet through a specific IP? Without the correct proxy configuration, Docker simply won't be able to download images from Docker Hub or any other registry. In this article, we will explore all levels of proxying β from the daemon to individual containers.
Why Docker Needs a Proxy
Docker is a tool that constantly interacts with external resources. With every docker pull or docker build, the daemon accesses Docker Hub, GitHub Container Registry, Google Container Registry, or your private registry. This is where problems begin.
Situations where a proxy is essential:
- Corporate Network with Firewall β all traffic must go through the corporate proxy server, otherwise the connection is blocked.
- Geo-Restrictions β Docker Hub or specific registries are not accessible from your country or data center.
- Limited Server without Direct Internet Access β VPS in a closed loop where the internet is only available through a gateway.
- Control of Outgoing Traffic from Containers β you want applications inside containers to access the network through a specific IP, for example, for scraping, API requests, or testing geo-dependent content.
- Anonymization of Requests β hide the real IP of the server when accessing external services from the container.
- Rate Limiting β Docker Hub limits the number of pull requests for anonymous users (100 pulls every 6 hours). Using a proxy with IP rotation can bypass this limitation.
It is important to understand that Docker is not a single application, but a system of several components. The daemon (dockerd) runs on the host and pulls images. Containers are isolated processes with their own network settings. Therefore, configuring a proxy for the daemon and for containers are two different tasks that are solved differently.
Three Levels of Proxying in Docker
Before diving into configurations, it is necessary to understand the architecture. Docker has three independent levels, each requiring separate proxy configuration:
| Level | What It Does | Where to Configure |
|---|---|---|
| Docker Daemon | Downloads images (docker pull), accesses registries | systemd override or daemon.json |
| Docker Build | Executes commands during image build (RUN apt-get, pip install, etc.) | ARG and ENV in Dockerfile or --build-arg |
| Runtime Container | Running application that makes HTTP requests | ENV during docker run or in docker-compose.yml |
A common mistake is to configure the proxy only at one level and wonder why images still do not pull or the application does not see the proxy. Letβs examine each level in detail.
Configuring Proxy for Docker Daemon (Pulling Images)
This is the most important configuration if you want to pull images through a proxy. The Docker daemon is a system service that runs through systemd. It does not see your user's environment variables or even root. You need to pass the proxy specifically to the service's environment.
Method 1: Through systemd override (recommended)
Create a directory for overriding service settings:
sudo mkdir -p /etc/systemd/system/docker.service.d
Create a file /etc/systemd/system/docker.service.d/http-proxy.conf with the following content:
[Service] Environment="HTTP_PROXY=http://username:password@proxy-host:port" Environment="HTTPS_PROXY=http://username:password@proxy-host:port" Environment="NO_PROXY=localhost,127.0.0.1,::1,your-private-registry.example.com"
If the proxy does not require authentication, simply remove username:password@. After creating the file, reload the systemd configuration and restart Docker:
sudo systemctl daemon-reload sudo systemctl restart docker
Check that the settings have been applied:
sudo systemctl show --property=Environment docker
Your HTTP_PROXY and HTTPS_PROXY variables should appear in the output.
Method 2: Through ~/.docker/config.json (Docker Desktop and newer versions)
Starting from Docker Engine 23.0, there is a more convenient way β configuring the proxy through the client configuration file. Create or edit the file ~/.docker/config.json:
{
"proxies": {
"default": {
"httpProxy": "http://username:password@proxy-host:port",
"httpsProxy": "http://username:password@proxy-host:port",
"noProxy": "localhost,127.0.0.1,::1"
}
}
}
This method is convenient as it does not require root privileges and does not require restarting the daemon. The settings are automatically passed to containers during build as build arguments. However, to manage pull requests from the daemon itself, the systemd method is still required.
π‘ Important about NO_PROXY
Always add your internal registry and service addresses to NO_PROXY. Otherwise, Docker will attempt to reach them through the proxy and receive connection errors. A typical list includes: localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
Proxy Inside the Container During Build (Build-Time)
When Docker builds an image and executes commands like RUN apt-get install, RUN pip install, or RUN npm install β these commands are executed inside a temporary container. By default, it does not have access to the host's proxy. You need to explicitly pass the proxy through build arguments.
Passing Proxy Through --build-arg
docker build \ --build-arg HTTP_PROXY=http://proxy-host:port \ --build-arg HTTPS_PROXY=http://proxy-host:port \ --build-arg NO_PROXY=localhost,127.0.0.1 \ -t my-image .
Configuration in Dockerfile
If the proxy is only needed during the build phase and you do not want it to be included in the final image β use ARG instead of ENV:
FROM ubuntu:22.04 # Declare build arguments ARG HTTP_PROXY ARG HTTPS_PROXY ARG NO_PROXY # Use them in commands RUN apt-get update && apt-get install -y curl wget # After this block, the proxy will not be in the container's ENV
If the proxy is needed in the running container as well β use ENV:
FROM python:3.11 ENV HTTP_PROXY=http://proxy-host:port ENV HTTPS_PROXY=http://proxy-host:port ENV NO_PROXY=localhost,127.0.0.1 RUN pip install requests beautifulsoup4 CMD ["python", "app.py"]
β οΈ Attention: Security
Do not hardcode the proxy username and password directly in the Dockerfile if the image will be public or will be stored in a repository. Use --build-arg and pass values from CI/CD environment variables. The command docker history can show ARG values, so for secrets, use Docker BuildKit secrets.
Proxy for Running Containers (Runtime)
This is the most common scenario for applications that make HTTP requests during operation β scrapers, bots, microservices that need to exit through a specific IP. Here, the configuration is as simple as possible: you need to pass environment variables when starting the container.
Through docker run
docker run \ -e HTTP_PROXY=http://username:password@proxy-host:port \ -e HTTPS_PROXY=http://username:password@proxy-host:port \ -e NO_PROXY=localhost,127.0.0.1 \ my-image
Most popular HTTP libraries automatically pick up the HTTP_PROXY and HTTPS_PROXY variables: Python requests, curl, wget, Go's net/http, Node.js https-proxy-agent, and others. No additional code needs to be specified.
Through .env file
It is more convenient to store settings in a .env file and pass it entirely:
# .env file HTTP_PROXY=http://username:password@proxy-host:port HTTPS_PROXY=http://username:password@proxy-host:port NO_PROXY=localhost,127.0.0.1
docker run --env-file .env my-image
SOCKS5 Proxy in the Container
If you are using a SOCKS5 proxy (for example, residential proxies most often support this protocol), the syntax is slightly different:
docker run \ -e ALL_PROXY=socks5://username:password@proxy-host:port \ my-image
Note: not all libraries support SOCKS5 through the environment variable without additional dependencies. Python requests requires installing requests[socks], and curl must be built with support for libcurl-socks.
Proxy in Docker Compose
In real projects, raw docker run is rarely used. Most often, work is done with Docker Compose, where multiple services are described in one file. Proxy configuration here is done at the level of each service or through a variable file.
Option 1: Environment in docker-compose.yml
version: '3.8'
services:
scraper:
image: my-scraper:latest
environment:
- HTTP_PROXY=http://username:password@proxy-host:port
- HTTPS_PROXY=http://username:password@proxy-host:port
- NO_PROXY=localhost,127.0.0.1
restart: unless-stopped
api:
image: my-api:latest
# This service without proxy β accesses only internal resources
ports:
- "8080:8080"
Option 2: Through .env file (recommended)
Create a .env file in the directory with docker-compose.yml. Docker Compose automatically picks up this file:
# .env PROXY_HOST=proxy-host PROXY_PORT=8080 PROXY_USER=username PROXY_PASS=password
version: '3.8'
services:
scraper:
image: my-scraper:latest
environment:
- HTTP_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
- HTTPS_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
- NO_PROXY=localhost,127.0.0.1
Proxy During Build in Compose
If there is a build section in Compose and you need to pass the proxy during the build phase:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
HTTP_PROXY: http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
HTTPS_PROXY: http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
environment:
- HTTP_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
- HTTPS_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
Which Type of Proxy to Choose for Docker
The choice of proxy type depends on the task. For Docker environments, three main types are relevant, each with its niche:
| Proxy Type | Speed | Anonymity | Best Scenario for Docker |
|---|---|---|---|
| Datacenter | β‘ High | Medium | Pulling images, CI/CD pipelines, bypassing Docker Hub rate limiting |
| Residential | π Medium | High | Scraping sites with protection, APIs with geo-restrictions, testing |
| Mobile | π Medium | Maximum | Applications working with mobile APIs (Instagram, TikTok) |
For pulling images and CI/CD, datacenter proxies are optimal β they provide maximum speed when downloading large images (several gigabytes) and a stable connection for long builds.
For scraper containers, which need to bypass site protection and appear as regular users, residential proxies are better suited. They have IPs of real home users, which reduces the likelihood of blocking even with intensive requests.
For applications working with mobile platforms β Instagram Graph API, TikTok API, mobile versions of services β consider mobile proxies. They use IPs from mobile operators and raise minimal suspicion with anti-bot systems.
Protocols: HTTP vs SOCKS5
The Docker daemon only supports HTTP/HTTPS proxies β SOCKS5 does not work for pulling images. If you have a SOCKS5 proxy and need to download images, you will have to use a local converter like privoxy or microsocks, which will accept HTTP and proxy through SOCKS5.
For runtime containers, the situation is better: most HTTP libraries directly support SOCKS5 through the ALL_PROXY=socks5://... variable.
Common Errors and How to Fix Them
Letβs discuss the most common problems encountered when configuring proxies in Docker:
Error 1: Proxy is configured on the host, but Docker does not see it
Symptom:
Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp: connection refused
Cause: The Docker daemon runs as a system service and does not inherit the environment variables of the current user, even if you set export HTTPS_PROXY=... in the terminal.
Solution: Configure the proxy through systemd override (method 1 from the section above). Be sure to execute systemctl daemon-reload && systemctl restart docker.
Error 2: apt-get / pip / npm do not work during build
Symptom:
Err:1 http://archive.ubuntu.com/ubuntu focal InRelease
Could not connect to archive.ubuntu.com:80
Cause: The proxy configuration for the daemon does not automatically propagate to commands inside RUN in the Dockerfile. These are two different contexts.
Solution: Pass the proxy through --build-arg or use ~/.docker/config.json with the proxies section (Docker 23.0+, automatically passes arguments during build).
Error 3: Internal services are not accessible through the proxy
Symptom:
curl: (7) Failed to connect to internal-service.local port 8080: Connection refused
Cause: The proxy attempts to proxy requests to internal addresses that are not accessible from the outside.
Solution: Add all internal domains and subnets to NO_PROXY. For corporate networks, a typical list includes: localhost,127.0.0.1,::1,.internal,.local,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
Error 4: Proxy authentication does not work β special characters in password
If the password contains special characters (@, #, %), the URL is parsed incorrectly.
Solution: URL-encode the password. For example, p@ss#word β p%40ss%23word. In Python, you can quickly get the encoded version:
python3 -c "from urllib.parse import quote; print(quote('p@ss#word', safe=''))"
# Output: p%40ss%23word
Error 5: Proxy works, but SSL certificate is not verified
Corporate proxies often perform SSL inspection (MITM) and replace certificates. In this case, Docker throws an error:
x509: certificate signed by unknown authority
Solution: Add the corporate root certificate to trusted on the host and restart Docker. On Ubuntu/Debian:
sudo cp corporate-ca.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates sudo systemctl restart docker
Error 6: Kubernetes / Docker Swarm β proxy does not work on all nodes
In clustered environments, the proxy needs to be configured on each node separately. For Kubernetes, you also need to configure environment variables in pod manifests or through ConfigMap. This can be automated using Ansible or other configuration management tools.
Conclusion
Configuring a proxy in Docker is not a single setting, but three independent levels: the daemon for pulling images, build-time for building, and runtime for running containers. By understanding this architecture, you can flexibly manage which traffic goes through the proxy and which goes directly.
A brief checklist for proper configuration:
- β For pulling images β configure systemd override with HTTP_PROXY and HTTPS_PROXY variables
- β
For building β pass the proxy through
--build-argor~/.docker/config.json - β
For runtime β use environment variables via
-eor--env-file - β Always configure NO_PROXY for internal addresses
- β Do not store proxy passwords in Dockerfile β use build-args and .env files
- β For CI/CD pipelines, choose fast datacenter proxies
- β For scraper containers β residential or mobile proxies
If your container applications make requests to external services, scrape data, or work with APIs that have geo-restrictions, we recommend using residential proxies β they provide a high level of trust from services and minimal risk of blocking even with intensive work.