Docker has long been the standard for running parsers, bots, ad automation, and small services. But containers have a quirk: they live in an isolated environment and know nothing about the proxies you've configured on your computer. As a result, a script inside a container goes online with your real IP, while you think it's working through a mobile proxy. This guide closes that gap once and for all.

Introduction: what you'll get and who this guide is for

After going through this guide, you'll be able to configure proxies in Docker at all three levels where it's even possible: for a single container via environment variables, for the entire Docker client and daemon via config files, and at the network level through a separate gateway container. You'll understand how these levels differ, when to use each one, and how to confirm that traffic is actually going through the proxy and not around it.

Who this step-by-step guide is for

  • Marketers and SMM specialists who run autoposting, analytics, or monitoring services in Docker and want each tool to work with its own mobile IP.
  • Affiliate marketers who have dozens of containers with trackers, offer parsers, and spy services, and each one needs its own geo.
  • Developers who need to test an app from another region or run integration tests through a proxy.
  • Business owners whose employees or contractors deploy infrastructure in containers, and who need to understand how proxy handling works there.

What you need to know beforehand

We don't assume deep knowledge. It's enough if you can open a terminal, copy a command, and read what it output. If you've never worked with Docker, don't worry: in the basic concepts section we'll explain all the terms in plain language. We're deliberately not touching Kubernetes, cluster orchestration, or cloud platforms: that's a separate topic, and here we stay strictly at the Docker level.

How much time it will take

A full walkthrough with checks will take 60 to 120 minutes. If you only need one scenario, for example a proxy for a single container, 15 minutes is enough. If Docker isn't installed yet, add 20–30 minutes for installation.

Preparation: tools, access, and system requirements

Before configuring a proxy in Docker, gather everything you need. That way you won't get distracted hunting for a login or installing utilities in the middle of the process.

What you'll need

  1. A computer or server with Docker. Linux (Ubuntu 22.04 or 24.04, Debian 12) will do, as will macOS with Docker Desktop or Windows 10/11 with Docker Desktop and WSL2. In 2026, Docker Engine version 27 and above and Docker Compose v2 are current, invoked with the command docker compose (with a space, no hyphen).
  2. Mobile proxy credentials. You need four things: the host address (IP or domain name), the port, the login, and the password. You'll find them in your provider's dashboard. Also check which protocol is available: HTTP or SOCKS5. Most mobile proxy providers, including mobileproxy.space, offer both on different ports.
  3. A terminal. On Linux and macOS it's built in. On Windows, use PowerShell or the WSL2 terminal (the second option is more convenient because the commands will be identical to Linux).
  4. A text editor. Any will do: nano, vim, VS Code, Notepad++. You need it for editing config files.
  5. The curl utility. Usually already installed. It'll help you check which IP your traffic is coming out from.

System requirements

  • At least 2 GB of RAM and 10 GB of free disk space for Docker and images.
  • Administrator rights: on Linux that means sudo access, on Windows and macOS an administrator account to install Docker Desktop.
  • A stable internet connection for downloading images.

Backups

Along the way we'll be editing Docker config files. A mistake in them can prevent Docker from starting. So before editing any file, make a copy. On Linux it's a single command:

sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak

The same goes for the ~/.docker/config.json file. If the file doesn't exist yet, no backup is needed, but make a note that you created it from scratch: then to roll back, simply delete it.

Tip: Keep a text file with a template of your proxy credentials in the format protocol://login:password@host:port. You'll paste this string many times, and a ready template will save you from typos.

Basic concepts: how Docker works and where the proxy lives in it

So that configuring a proxy in Docker doesn't turn into magic, let's go over the terms. If you already work confidently with containers, skim the section quickly, but pay attention to the subsection about the three proxy levels: that's where most mistakes hide.

Key terms in plain language

  • Image — a template, a "frozen" set of files and programs. For example, a Python image or a browser image.
  • Container — a running copy of an image. You can launch as many containers from one image as you like, and each will be isolated from the others.
  • Docker daemon (dockerd) — the background service that creates containers, downloads images, and manages networks. It's the daemon that goes online for images when you type docker pull.
  • Docker client (docker CLI) — the docker command in your terminal. It sends your orders to the daemon.
  • Environment variables — named values available to programs inside a container. For example, HTTP_PROXY=http://user:pass@host:port. Many programs automatically read such variables and start going through the specified proxy.
  • Docker network — a virtual network that connects containers. Containers in the same user-defined network see each other by name.
  • Docker Compose — a tool that describes several containers, their variables, and networks in a single YAML file and launches them with one command.

The three proxy levels in Docker

This is the most important part of the theory. When people say "proxy in Docker," they can mean three completely different things, and they're configured differently.

  1. Proxy for the daemon. Needed so Docker itself downloads images through a proxy. This is about the docker pull and docker build commands when they pull base images. This level doesn't affect the traffic of your applications inside containers.
  2. Proxy for containers via environment variables. HTTP_PROXY, HTTPS_PROXY, and NO_PROXY are passed into the container, and the application itself decides whether to use them. This is the most popular and simplest method, but it only works with programs that respect these variables.
  3. Proxy at the network level. A container's traffic is routed through another gateway container or through a specially configured network. The application inside may not even know about the proxy. This is more reliable but requires more setup.

What's important to understand before you start

Environment variables with a proxy are just a recommendation for the program. The curl utility, the pip package manager, Python's requests library, Node.js with the global-agent package, wget, apt — they all read HTTP_PROXY. But headless browsers, some Go applications, and many binary utilities may ignore the variables. So after configuring, always check the actual external IP rather than assuming the variable is set.

Another nuance is case. Historically, some programs read http_proxy in lowercase, others HTTP_PROXY in uppercase. The reliable practice is to set both at the same time. The NO_PROXY variable lists addresses for which no proxy should be used: localhost, 127.0.0.1, internal domains, neighboring container names.

Finally, the proxy address format. For an HTTP proxy, the string looks like http://login:password@host:port. For SOCKS5 — socks5://login:password@host:port or socks5h://login:password@host:port. The letter h at the end means DNS queries also go through the proxy, which for mobile proxies is usually preferable: that way the target site won't see your provider's DNS resolver.

Step 1: Verifying Docker and preparing proxy credentials

Goal of this stage: make sure Docker is running and your proxy credentials are correct and accessible from this computer. Without this check, you risk spending half an hour hunting for an error in the config when the problem was a typo in the password.

Verifying Docker

  1. Open your terminal.
  2. Type docker --version and press Enter. You should see a line like Docker version 27.x.x. If the terminal says the command isn't found, Docker isn't installed: install Docker Desktop (Windows, macOS) or Docker Engine (Linux) per the official documentation and come back here.
  3. Type docker compose version. Expected output: Docker Compose version v2.x.x.
  4. Type docker run --rm hello-world. Docker will download a tiny test image and print a greeting with the words Hello from Docker. This means the daemon is working and you have permission to run containers.

Tip: If on Linux the docker command requires sudo, add yourself to the docker group: sudo usermod -aG docker $USER, then log out and log back in. From then on all commands in this guide will work without sudo.

Verifying the proxy from the host

Before carrying the proxy into a container, let's confirm it responds at all. Substitute your own credentials for the example. In the examples we'll use the address 185.10.10.10, port 1050 for HTTP and 1051 for SOCKS5, login user123, and password secret. You'll naturally have your own values.

  1. First find out your regular IP without a proxy: curl -s ifconfig.me. Write down or remember the result.
  2. Now a request through the HTTP proxy: curl -s -x http://user123:secret@185.10.10.10:1050 ifconfig.me
  3. If you have SOCKS5: curl -s -x socks5h://user123:secret@185.10.10.10:1051 ifconfig.me
  4. Compare the result with step 1. The IP should differ and belong to a mobile carrier.

Special characters in the password

If your login or password contains the characters @, :, /, #, ? or a space, they must be URL-encoded, otherwise the proxy string will break. The @ character becomes %40, : becomes %3A, / becomes %2F, # becomes %23, ? becomes %3F, a space becomes %20. For example, the password pa@ss in a proxy string is written as pa%40ss.

✅ Check: The curl command through the proxy returned an IP different from your home one, and the response came back in one to three seconds. If you got a 407 error, check the login and password. If it's Connection refused or a timeout, check the host, port, and whether your current IP is added to the whitelist in your provider's dashboard (on some plans, IP-based authorization is enabled by default).

Step 2: Configuring a proxy for a single container via environment variables

Goal of this stage: launch a container whose entire HTTP traffic goes through a mobile proxy, and confirm it by the external IP. This is the basic scenario everyone should start with: it doesn't touch system settings and is easy to undo.

Launching with the -e flag

The -e flag (or --env) of the docker run command passes an environment variable into the container. We'll pass four variables at once: proxy for HTTP, for HTTPS, and exclusions, each in two cases.

  1. Copy the command below into an editor and replace the proxy credentials with your own.
  2. Run the command in the terminal. It will launch a temporary container with curl that makes a request and exits.
docker run --rm -e HTTP_PROXY=http://user123:secret@185.10.10.10:1050 -e HTTPS_PROXY=http://user123:secret@185.10.10.10:1050 -e http_proxy=http://user123:secret@185.10.10.10:1050 -e https_proxy=http://user123:secret@185.10.10.10:1050 -e NO_PROXY=localhost,127.0.0.1 -e no_proxy=localhost,127.0.0.1 curlimages/curl -s ifconfig.me

Note: for HTTPS_PROXY we also specify http:// at the start. That's not a mistake. This sets the proxy through which HTTPS requests will go, while the connection to the proxy server itself is ordinary. A https:// scheme in the HTTPS_PROXY value would mean you have to connect to the proxy itself over TLS, which most providers don't support.

A file with variables instead of a long command

The command came out bulky. Docker can read variables from a file via the --env-file flag. This is more convenient and safer: the password doesn't stay in the terminal history.

  1. Create a proxy.env file in your working folder: nano proxy.env
  2. Write the lines into it, one variable per line, without quotes and without spaces around the equals sign:
HTTP_PROXY=http://user123:secret@185.10.10.10:1050 HTTPS_PROXY=http://user123:secret@185.10.10.10:1050 http_proxy=http://user123:secret@185.10.10.10:1050 https_proxy=http://user123:secret@185.10.10.10:1050 NO_PROXY=localhost,127.0.0.1 no_proxy=localhost,127.0.0.1

In a real file, each variable should be on its own line. Save the file (in nano that's Ctrl+O, Enter, then Ctrl+X) and launch the container:

docker run --rm --env-file proxy.env curlimages/curl -s ifconfig.me

Checking from inside a running container

Often you need to see what a long-running container sees. Let's launch Alpine Linux in interactive mode and check the variables.

  1. Run docker run -it --rm --env-file proxy.env alpine sh. You'll be inside the container, and the prompt will change to a hash or dollar sign.
  2. Type env | grep -i proxy. You'll see the list of your variables.
  3. Type apk add --no-cache curl. The apk package manager will pick up https_proxy itself and download the package through the proxy.
  4. Type curl -s ifconfig.me and make sure the IP is mobile.
  5. Type exit to leave. The container will be deleted automatically thanks to the --rm flag.

Tip: For checking the IP, besides ifconfig.me it's handy to use services that return JSON with country, city, and provider info. That way you'll immediately see that the IP belongs to a mobile carrier in the right region, rather than just "some other one."

✅ Check: Both curl runs returned the mobile proxy IP. The env command inside the container showed the HTTP_PROXY and HTTPS_PROXY variables with your credentials.

Possible problems at this step

  • The IP didn't change. The application in the container is ignoring the variables. That never happens with curl, so if curl shows a mobile IP but your app doesn't, move on to the network method in step 6.
  • invalid reference format error. Usually this is an extra space or a line break in the command. Put the command together on one line.
  • Variables aren't visible. The env-file must not have quotes around the values: Docker will pass them literally, and the proxy address will become incorrect.

Step 3: Configuring a proxy for the Docker client so variables are passed automatically

Goal of this stage: make every new container and every image build automatically receive the proxy variables without -e flags. This saves time if you constantly launch different containers through the same mobile proxy.

How it works

The Docker client reads the config.json file in the ~/.docker folder (on Windows, that's the .docker folder in the user profile). If it has a proxies section, the client adds the specified variables to the container on every docker run and docker build. The daemon isn't affected, so docker pull will still go directly.

Step-by-step setup

  1. Check whether the file exists: cat ~/.docker/config.json. If the file exists and already has settings (for example, auths with registry login data), make a copy: cp ~/.docker/config.json ~/.docker/config.json.bak
  2. Open the file in an editor: nano ~/.docker/config.json. If the file doesn't exist, the editor will create it.
  3. Add the proxies section. If the file was empty, its entire content will be:
{ "proxies": { "default": { "httpProxy": "http://user123:secret@185.10.10.10:1050", "httpsProxy": "http://user123:secret@185.10.10.10:1050", "noProxy": "localhost,127.0.0.1,*.local" } } }

If the file already had other keys, add proxies as another top-level key separated by a comma, without removing the existing ones. Keep an eye on matching curly braces and quotes: JSON doesn't forgive a missing comma.

  1. Save the file.
  2. Check the syntax. On Linux and macOS this is handy: python3 -m json.tool ~/.docker/config.json. If the output repeats your file nicely formatted, all is well. If an error with a line number appears, fix it.
  3. Run a test container without any flags: docker run --rm curlimages/curl -s ifconfig.me. The IP should be mobile.
  4. Look at the variables of any container: docker run --rm alpine env. The output will have HTTP_PROXY, HTTPS_PROXY, NO_PROXY, and their lowercase variants: Docker adds both cases itself.

⚠ Warning: The proxies section in config.json affects all containers you launch from this user, including databases, local web servers, and everything else. If some service talks to an external API that isn't accessible through your proxy, it'll break. Add such addresses to noProxy or temporarily remove the section.

Different proxies for different connections

The default key applies to all connections to the daemon. If you manage several Docker hosts via contexts or the DOCKER_HOST variable, instead of default you can specify the address of a specific daemon, for example tcp://192.168.1.50:2376, and the proxy will apply only to it. For local work, default is enough.

Proxy during image builds

Settings from config.json are also passed into docker build as build arguments. This means RUN apt-get install or RUN pip install commands inside the Dockerfile will go through the proxy. Important point: these variables are not saved in the resulting image, which is good from a security standpoint: the proxy password won't leak to those you hand the image to.

Tip: If you want to pass the proxy only for one build without touching config.json, use the flags docker build --build-arg HTTP_PROXY=http://user123:secret@185.10.10.10:1050 --build-arg HTTPS_PROXY=http://user123:secret@185.10.10.10:1050 . Docker understands these predefined arguments without declaring ARG in the Dockerfile.

✅ Check: A container launched without -e flags goes online with the proxy IP. The docker run --rm alpine env command shows the proxy variables.

How to roll back

Remove the proxies section from config.json or restore the file from backup: cp ~/.docker/config.json.bak ~/.docker/config.json. No restart is needed; changes apply to the next container launch.

Step 4: Configuring a proxy for the Docker daemon so images are downloaded through the proxy

Goal of this stage: make Docker itself (the daemon) fetch images through a proxy. This is needed when direct access to the image registry from your server is restricted by corporate policy, slow, or when you want all network activity from the server to go through a single channel. For marketing tasks this step is often unnecessary, but it's worth knowing: daemon-level errors are regularly confused with container-level ones.

Method 1: the daemon.json file (Linux, Docker 23 and newer)

  1. Make a copy: sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak (if the file doesn't exist, the command will error out, that's normal).
  2. Open the file: sudo nano /etc/docker/daemon.json
  3. Add the proxies section:
{ "proxies": { "http-proxy": "http://user123:secret@185.10.10.10:1050", "https-proxy": "http://user123:secret@185.10.10.10:1050", "no-proxy": "localhost,127.0.0.1" } }

Note that the keys here are hyphenated and lowercase: this differs from the client's config.json, where the keys are styled like httpProxy. Mixing them up is a classic mistake.

  1. Save the file and restart the daemon: sudo systemctl restart docker
  2. Check that the daemon came up: sudo systemctl status docker. The output should say active (running).
  3. Check that it applied: docker info | grep -i proxy. You'll see HTTP Proxy and HTTPS Proxy lines with your address, and the password will be hidden with asterisks in the output.

Method 2: a systemd drop-in file (Linux, any version)

This is the classic method that works even on older Docker versions.

  1. Create the folder: sudo mkdir -p /etc/systemd/system/docker.service.d
  2. Create the file: sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
  3. Write the content, each directive on its own line:
[Service] Environment="HTTP_PROXY=http://user123:secret@185.10.10.10:1050" Environment="HTTPS_PROXY=http://user123:secret@185.10.10.10:1050" Environment="NO_PROXY=localhost,127.0.0.1"
  1. Save, then reload the systemd configuration: sudo systemctl daemon-reload
  2. Restart Docker: sudo systemctl restart docker
  3. Check: sudo systemctl show --property=Environment docker. The output will show your variables.

⚠ Warning: Don't configure the daemon proxy with both methods at the same time. If both daemon.json and the systemd file contain different addresses, the behavior becomes unpredictable and debugging becomes painful. Pick one method and stick with it.

Method 3: Docker Desktop (Windows and macOS)

  1. Open Docker Desktop and click the gear icon in the top right corner.
  2. In the left menu, select Resources, then Proxies.
  3. Toggle Manual proxy configuration on.
  4. In the Web Server (HTTP) and Secure Web Server (HTTPS) fields, paste the proxy address in the format http://user123:secret@185.10.10.10:1050.
  5. In the Bypass proxy settings for these hosts field, enter localhost,127.0.0.1.
  6. Click Apply and restart. Docker Desktop will restart, which takes 30–60 seconds.

Docker Desktop applies these settings to both the daemon and the containers at once, so separately editing config.json on desktop systems is often unnecessary.

Verifying the result

  1. Delete some small image if you have one: docker rmi alpine
  2. Download it again: docker pull alpine. The download should succeed.
  3. If your proxy has traffic statistics (mobileproxy.space has them in the dashboard), you'll see the consumed traffic volume grew by a few megabytes.

✅ Check: docker info shows the proxy address, docker pull downloads images without errors, and the docker service is in the active state.

Possible problems

  • Docker won't start after editing daemon.json. Almost always the JSON syntax is at fault. Check the file with python3 -m json.tool /etc/docker/daemon.json or restore the copy.
  • docker pull hangs. The proxy isn't allowing connections to the registry, or the plan's traffic limit has been exceeded. Check the proxy from the host via curl, as in step 1.
  • x509 certificate error. The proxy is substituting certificates (relevant for corporate proxies, rare for mobile ones). Check with your provider.

Step 5: Configuring a proxy in Docker Compose

Goal of this stage: describe the proxy in a compose.yaml file so a group of containers launches with one command with the right settings, and different services can use different mobile proxies. This is exactly the scenario affiliate marketers and marketers usually need: one parser works through a Moscow proxy, a second through a Kazan proxy, and the database runs without a proxy at all.

Preparing the project

  1. Create a project folder and go into it: mkdir proxy-demo, then cd proxy-demo
  2. Create a .env file (with the leading dot) to store secrets: nano .env
  3. Write the variables, one per line:
PROXY_MSK=http://user123:secret@185.10.10.10:1050 PROXY_KZN=http://user456:secret2@185.10.10.20:1050

Compose reads the .env file automatically, and its values can be substituted into compose.yaml via the ${NAME} syntax. Add .env to .gitignore if the project is under version control: passwords shouldn't end up in the repository.

The compose.yaml file

Create a compose.yaml file (nano compose.yaml) and describe three services. In YAML, indentation matters: use two spaces per level, no tabs.

services: parser-msk: image: curlimages/curl command: -s ifconfig.me environment: HTTP_PROXY: ${PROXY_MSK} HTTPS_PROXY: ${PROXY_MSK} http_proxy: ${PROXY_MSK} https_proxy: ${PROXY_MSK} NO_PROXY: localhost,127.0.0.1,db no_proxy: localhost,127.0.0.1,db parser-kzn: image: curlimages/curl command: -s ifconfig.me environment: HTTP_PROXY: ${PROXY_KZN} HTTPS_PROXY: ${PROXY_KZN} http_proxy: ${PROXY_KZN} https_proxy: ${PROXY_KZN} NO_PROXY: localhost,127.0.0.1,db no_proxy: localhost,127.0.0.1,db db: image: postgres:16-alpine environment: POSTGRES_PASSWORD: example

Here, in the real file, each line stands separately with the correct indentation: services at the zero level, service names indented two spaces, their parameters four, and environment variables six. Note the name db in NO_PROXY: that way the parsers will reach the database directly over Docker's internal network rather than trying to reach it through the mobile proxy, which would never work.

Launch and verification

  1. Check how Compose substituted the variables: docker compose config. The command will output the final file with expanded values. Make sure a real address is in place instead of ${PROXY_MSK}.
  2. Launch: docker compose up. Compose will download the images and start all three services, outputting their logs to the terminal.
  3. In the logs you'll see lines like parser-msk-1 | 91.xxx.xxx.xxx and parser-kzn-1 | 176.xxx.xxx.xxx: two different IPs from two different proxies. Postgres will start and wait for connections.
  4. Stop everything with Ctrl+C, then remove the containers: docker compose down

Alternative: env_file per service

If there are many variables, instead of the environment block it's more convenient to specify a file:

services: parser-msk: image: curlimages/curl command: -s ifconfig.me env_file: - proxy-msk.env

The proxy-msk.env file then contains the same six lines as proxy.env from step 2. This way each proxy lives in its own file, and you can replace it without opening compose.yaml.

Proxy during builds in Compose

If a service is built from a Dockerfile rather than taken from a ready image, pass the proxy into the build via build.args:

services: app: build: context: . args: HTTP_PROXY: ${PROXY_MSK} HTTPS_PROXY: ${PROXY_MSK}

That way pip, npm, or apt inside the Dockerfile will work through the proxy, and the values won't end up in the resulting image.

Tip: Compose supports multiple files. Keep a base compose.yaml without a proxy, and in compose.proxy.yaml describe only the environment blocks. Launch docker compose -f compose.yaml -f compose.proxy.yaml up when you need the proxy, and just docker compose up when you don't. This is handy for debugging: you switch between modes in a second.

✅ Check: docker compose config shows the substituted proxy addresses, and in the docker compose up logs services with different proxies output different IPs.

Step 6: Configuring a proxy at the network level through a gateway container

Goal of this stage: bring up a separate container that accepts connections from neighbors on the Docker network and forwards them into a mobile proxy. The other containers reach the gateway by name and don't store the login and password themselves. This solves three problems at once: it centralizes proxy management, removes passwords from dozens of configs, and lets you change the proxy without restarting the working containers.

Why a gateway is needed if you have variables

Imagine you have twenty parser containers and the provider issued a new port. With environment variables you'd have to edit twenty configs and restart everything. With a gateway, you change one line in one place. Besides, some applications don't support proxy login/password authorization but work fine with a proxy that has no authorization. A gateway inside a closed Docker network requires no authorization, and connects to the mobile proxy itself with your credentials.

Creating the network

  1. Create a user-defined network: docker network create proxynet
  2. Make sure it appeared: docker network ls. The list will show proxynet with the bridge driver.

A user-defined network is needed because name resolution only works there: a container will be able to reach the gateway by the name gateway rather than by an IP that changes with every restart.

Launching the gateway

We'll use gost as the gateway — a compact proxy server that can accept connections on one protocol and forward them to another with authorization. The image is available in the public registry under the name gogost/gost.

  1. Launch the gateway container:
docker run -d --name gateway --network proxynet --restart unless-stopped gogost/gost -L=http://:8118 -F=http://user123:secret@185.10.10.10:1050

Let's break down the parameters. The -d flag launches the container in the background. The --name gateway flag sets the name by which neighbors will reach it. The --network proxynet flag connects it to our network. The --restart unless-stopped flag brings the gateway back up after a server reboot. The -L=http://:8118 parameter tells gost to accept HTTP proxy connections on port 8118 without authorization. The -F parameter specifies where to forward: to your mobile proxy with login and password.

  1. Check that the gateway is working: docker logs gateway. The log should have a line saying the server is listening on port 8118, with no errors.

⚠ Warning: Don't expose the gateway's port to the outside with the -p flag unless there's a direct need. The gateway works without authorization, and an open port 8118 on a public server means anyone on the internet can use your mobile proxy and burn through your traffic. Inside the proxynet network it's accessible only to your containers, and that's enough.

Connecting the working containers

  1. Launch a test container on the same network, specifying the gateway as the proxy:
docker run --rm --network proxynet -e HTTP_PROXY=http://gateway:8118 -e HTTPS_PROXY=http://gateway:8118 curlimages/curl -s ifconfig.me
  1. You should see the mobile proxy IP. Notice: the variables contain no login, no password, no real proxy address. Only the gateway knows all that.

The same thing in Compose

For continuous operation, describe the gateway and the working services in a single compose.yaml:

services: gateway: image: gogost/gost command: -L=http://:8118 -F=${PROXY_MSK} restart: unless-stopped networks: - proxynet worker: image: curlimages/curl command: -s ifconfig.me environment: HTTP_PROXY: http://gateway:8118 HTTPS_PROXY: http://gateway:8118 NO_PROXY: localhost,127.0.0.1 depends_on: - gateway networks: - proxynet networks: proxynet: driver: bridge

The depends_on directive guarantees the gateway starts before the worker. The PROXY_MSK value comes from the .env file, as in step 5.

Multiple gateways for multiple geos

Want different proxies for different container groups? Bring up several gateways: gateway-msk, gateway-kzn, gateway-spb, each with its own -F. Working containers simply specify the right name in HTTP_PROXY. You can go further and create a separate network per geo, then containers from the Moscow group physically can't accidentally hit the Kazan gateway.

Isolation: a container with no direct internet access

The strictest option is to forbid the working container any internet access except through the gateway. To do this, create an internal network with the --internal flag: docker network create --internal isolated. Containers on such a network have no route to the outside. Connect the gateway to two networks at once (isolated and the regular proxynet), and the workers only to isolated. Now even if the application ignores the proxy variables, it simply can't go online directly and no real IP will leak.

  1. docker network create --internal isolated
  2. docker network connect isolated gateway
  3. docker run --rm --network isolated -e HTTP_PROXY=http://gateway:8118 -e HTTPS_PROXY=http://gateway:8118 curlimages/curl -s ifconfig.me
  4. For control, run the same container without the proxy variables: docker run --rm --network isolated curlimages/curl -s -m 5 ifconfig.me. The request should time out: there's no direct route out.

Tip: The combination of an internal network and a gateway is the best insurance against leaks when working with multiple accounts. Even if a developer forgot to set the proxy in a new service, it won't be able to expose the server IP: it'll either go through the gateway or nowhere at all.

✅ Check: A container on the proxynet network with HTTP_PROXY=http://gateway:8118 shows the mobile proxy IP. A container on the internal network without a proxy can't go online at all.

Possible problems

  • Could not resolve host: gateway. The working container isn't on the right network or was launched on the default network, where names don't resolve. Check the --network flag.
  • The gateway keeps restarting. An error in the -F string: a typo in the password or a wrong port. Check docker logs gateway.
  • Slow. Mobile proxies are inherently slower than datacenter ones, but if the latency runs into tens of seconds, check whether DNS is bypassing: use socks5h instead of socks5 in the -F string if your provider offers SOCKS5.

Verifying the result: a checklist for a working proxy in Docker

Go through the list. If every item is checked, you've fully mastered configuring a proxy in Docker in practice.

Checklist

  • curl from the host through the proxy returns a mobile IP.
  • A container with the --env-file proxy.env flag returns a mobile IP.
  • A container without flags after configuring config.json returns a mobile IP (if you did step 3).
  • docker info shows the proxy address, and docker pull works (if you did step 4).
  • docker compose up launches the services, and the logs show different IPs for different proxies.
  • The gateway container works, and neighbors go through it without login and password.
  • A container on the internal network without a proxy can't go online.

How to test end-to-end

  1. Launch a long-running container: docker run -d --name test --network proxynet -e HTTP_PROXY=http://gateway:8118 -e HTTPS_PROXY=http://gateway:8118 alpine sleep 3600
  2. Go inside it: docker exec -it test sh
  3. Install curl: apk add --no-cache curl. The installation should go through the proxy.
  4. Make five requests in a row: for i in 1 2 3 4 5; do curl -s ifconfig.me; echo; done. All five should return the mobile IP. If your proxy has automatic rotation enabled, the IPs may differ between requests, which is normal.
  5. Exit (exit) and remove the container: docker rm -f test

Success indicators

A successful setup means you can answer three questions in a second: which IP a specific container is coming out from, where the proxy password is stored, and what needs to change to switch a container to a different proxy. If the answer to each question is obvious, the goal is achieved.

Common mistakes when configuring a proxy in Docker and how to fix them

Mistake 1: The IP doesn't change even though the variables are set

Cause: the application inside the container doesn't read the proxy environment variables. This is typical of headless browsers, some Go programs, and utilities that use their own network stacks.

Fix: check the application's documentation for its own proxy flag (for browsers it's usually --proxy-server). If there's no flag, use the gateway and internal network from step 6 or transparent proxying from the advanced section.

Mistake 2: 407 Proxy Authentication Required

Cause: a wrong login or password, or special characters in them aren't encoded, or the proxy has IP-based authorization enabled and the server IP isn't on the whitelist.

Fix: check the credentials from the host via curl. Encode special characters. Add the server IP to the whitelist in your dashboard or switch the proxy to login/password authorization.

Mistake 3: Docker won't start after editing daemon.json

Cause: a syntax error in the JSON: an extra comma, a missing quote, keys in config.json style instead of daemon.json style.

Fix: check the journal: sudo journalctl -u docker -n 50. It will point to the line with the error. Fix it or restore the backup and restart the service.

Mistake 4: containers stopped seeing each other

Cause: after globally configuring the proxy in config.json, requests to neighboring containers also went through the mobile proxy, which doesn't know what db or redis is.

Fix: add the service names and internal subnets to NO_PROXY: localhost,127.0.0.1,db,redis,172.16.0.0/12. Note that not all programs understand subnet masks, so listing names explicitly is more reliable.

Mistake 5: docker build fails on apt-get or pip

Cause: the build goes without a proxy because the variables are set for containers, not for the build, or the daemon proxy is configured but doesn't affect RUN steps.

Fix: pass --build-arg HTTP_PROXY and HTTPS_PROXY, or set up the proxies section in the client's config.json: it applies to builds too.

Mistake 6: the proxy password is visible in docker inspect and logs

Cause: environment variables are stored in the container's metadata in plain text, and anyone with Docker access will see them via docker inspect.

Fix: use the gateway: the working containers only know the address gateway:8118. The password stays in one container and in a .env file with restricted permissions (chmod 600 .env).

Mistake 7: after a server reboot the proxy stopped working

Cause: the gateway container wasn't launched with a restart policy, or the mobile proxy's host IP changed.

Fix: add --restart unless-stopped to the gateway. Use the proxy's domain name instead of the IP if the provider offers one. Check docker ps -a: if the gateway is in the Exited state, look at its logs.

Mistake 8: HTTPS sites don't open but HTTP works

Cause: only HTTP_PROXY is set and HTTPS_PROXY is empty, or HTTPS_PROXY specifies the https:// scheme instead of http://.

Fix: always set both variables with the same value and the http:// scheme.

Extra features for advanced users: transparent proxying, IP rotation, and security

This section is for those who've gone through the basic steps and want to squeeze the most out of the Docker and mobile proxy combo. Here there are fewer step-by-step lists and more ideas with key commands.

Transparent proxying: when the application doesn't know about the proxy at all

If you have an application that can't work with a proxy in any way, you can wrap all its TCP traffic at the network stack level. The idea is this: the working container launches with the parameter network_mode: service:gateway (in Compose) or --network container:gateway (in docker run). That way it uses the gateway container's network stack entirely: the same IP, the same interfaces, the same routing rules.

The gateway then runs a program like redsocks, which listens on a local port and forwards connections to the SOCKS5 proxy, while iptables rules redirect all outgoing TCP traffic to that port. The gateway needs permissions for this: cap_add: NET_ADMIN. The application in the working container makes an ordinary request to a site, the kernel intercepts it and routes it to redsocks, which sends it to the mobile proxy. No environment variables. The setup requires care: a wrong iptables rule can loop the traffic, so test on a separate machine. Also note that with network_mode: service the working container loses its own ports and connections to other networks; all of that has to be described on the gateway.

IP rotation from a container

Mobile proxies have a feature that's exactly why people use them: the IP can be changed on request. Providers offer a special rotation link that just needs to be opened for the modem to reconnect. From a container this is done with the same curl. A useful pattern: a separate little service in Compose that hits the rotation link on a schedule. It shouldn't go through the proxy (otherwise after the IP change it'll lose its own connection), so run it without proxy variables or with explicitly empty HTTP_PROXY. Note that after the IP change, active connections of the working containers will break: build retries into your parsers.

Gateway health: healthcheck

Add a check to Compose that the gateway is actually proxying, not just running:

healthcheck: test: ["CMD", "gost", "-V"] interval: 30s timeout: 10s retries: 3

This is a minimal liveness check. For checking real internet access, a separate monitor container is better: it makes a request through the gateway once a minute and writes the result to a log or sends a notification. If the IP suddenly becomes the server IP, that's an alarm: the gateway went down and the containers went direct. The internal network from step 6 is exactly what protects against that scenario.

Secure password storage

The .env file is fine for local work, but on a server with multiple users it's worth protecting: chmod 600 .env, owned by the user who launches Compose. Compose also supports secrets via the secrets directive, which are mounted into the container as a file in /run/secrets/ rather than as an environment variable. Gost doesn't read the password from a file directly, but you can write a small wrapper script that assembles the -F string from the secret file at startup. That way the password won't end up in docker inspect or in the output of docker compose config.

Multiple projects and one proxy infrastructure

If you have several Compose projects and the proxies are shared, move the gateways into a separate project with an external network: in it, declare networks with the name: proxynet parameter, and in the other projects connect to it as external: true. Then the gateways live independently, and the working projects can be restarted as much as you like without touching the proxies.

Traffic limits

Mobile traffic is usually metered, and one runaway parser can download tens of gigabytes overnight. Docker has no hard traffic quotas at the container level, but there are indirect measures: rate limiting requests in the application itself, a container lifetime limit via timeout in the launch command, and monitoring via docker stats, which shows NET I/O per container in real time. Regularly compare these numbers against the statistics in your provider's dashboard.

Logs without secrets

Many applications print environment variables to the log at startup, including HTTP_PROXY with the password. If the logs go to a centralized system, the password will leak there. The gateway solves this problem too: the working container logs will only show gateway:8118.

Tip: Rotate your proxy passwords once a quarter and update .env. With a gateway this takes a minute: edit one line, run docker compose up -d gateway, and all the workers keep running without a restart.

FAQ: common questions about configuring a proxy in Docker

Do I need to restart the container to apply new proxy variables?

Yes. Environment variables are set at container creation and can't be changed in a running one. Stop, remove, and recreate the container (in Compose that's docker compose up -d --force-recreate service_name). If restarts get in the way, use a gateway: its settings can be changed independently.

What's the difference between configuring a proxy for docker pull and for an application in a container?

These are two different levels. docker pull is performed by the daemon, and its proxy is set in daemon.json or via systemd. An application in a container is a separate process with its own environment; its proxy is set by variables, the client's config.json, or the network. One doesn't replace the other.

Can I use SOCKS5 instead of an HTTP proxy in the environment variables?

Yes, if the application supports SOCKS. curl, Python requests (with the PySocks package installed), and git support it. apt and many others don't. The universal solution: a gost gateway that accepts HTTP on the input and sends to SOCKS5 on the output: -L=http://:8118 -F=socks5://user:pass@host:port.

How do I check which proxy a running container is using?

Run docker inspect -f '{{.Config.Env}}' container_name. You'll see all the environment variables. To check the actual IP, use docker exec container_name curl -s ifconfig.me if curl is in the container, or wget -qO- ifconfig.me.

Why does the proxy work in Docker Desktop but the same settings don't work on a Linux server?

Docker Desktop applies the settings from the Proxies window to both the daemon and the containers at once. On Linux these are two separate places: daemon.json for the daemon and ~/.docker/config.json for the containers. Check that you configured both if you need both.

How do I set a proxy for only one domain and let everything else go direct?

Environment variables can't do that: they work on the principle of "everything through the proxy except NO_PROXY." If you need the reverse logic, use a PAC file on the application side (browsers support it) or routing rules in gost, which can route traffic to different outbound channels by domain.

Is it safe to store the proxy password in compose.yaml?

Better not. Store it in .env with 600 permissions and substitute it via ${NAME}. Don't commit .env to the repository. For servers, use a gateway so the password is in one place.

What should I do if the mobile proxy changed its IP and connections in the containers dropped?

This is normal behavior during rotation. The application should be able to retry requests. If rotation happens on the provider's schedule, find out the interval and sync heavy operations with it. If rotation is via your link, call it between batches of tasks, not in the middle.

Do these settings work on Windows without WSL2?

Docker Desktop on Windows uses WSL2 or Hyper-V under the hood, and all docker run and docker compose commands work the same from PowerShell. Only the paths differ: the config.json file is at C:\Users\UserName\.docker\config.json, and the daemon settings are done through the Docker Desktop window rather than by editing daemon.json manually.

How many containers can I run through one mobile proxy?

Technically, as many as you like; the only limit is the mobile channel's bandwidth and your plan's limits. In practice, for account-based tasks it's reasonable to keep one proxy per logical entity (account, project, region) so the behavior looks natural and an error in one container doesn't affect the others.

Conclusion: what you've done and where to go next

Let's sum up. You checked that the proxy works from the host and got to grips with the connection string format. You configured a proxy for a single container via environment variables and an env-file. You made variable passing automatic via the client's config.json. You learned how and why to configure a proxy for the Docker daemon itself in three ways. You described several services with different mobile proxies in Docker Compose, moving secrets into .env. And finally, you built a gateway container with an isolated network — the most reliable solution for production, which protects against real IP leaks even if the application ignores the variables.

Now a proxy in Docker is not a black box for you but three clear levels with distinct boundaries: daemon, container, network. You know where to look for the problem if the IP suddenly turns out to be wrong, and you can check it with a single command.

What to do next

  1. Move your working projects to the gateway and internal network scheme. Start with one non-critical service, make sure everything works, then scale up.
  2. Add a monitor container that checks the external IP through the gateway once a minute and alerts if it matches the server IP.
  3. Set up IP rotation on a schedule for your tasks and teach your applications to survive a dropped connection.
  4. Tidy up your secrets: .env with 600 permissions, no passwords in compose.yaml or Dockerfile.

Where to develop further

The next logical step is linking Docker with antidetect browsers and multi-accounting tools, where each profile corresponds to its own container and its own mobile proxy. Another direction is automation via the provider's API: getting a list of proxies, checking their status, and rotating right from your services. Both topics go beyond this guide, but the foundation you've just laid makes them significantly easier. Happy launches and stable IPs!