Clash runs fine on the host, and then everything inside a container times out. The reason is simple: a container has its own network namespace, so 127.0.0.1 means the container itself.
The core prerequisite: enable allow-lan
Whatever approach you take, step one is the same — change the address Clash listens on from 127.0.0.1 to 0.0.0.0:
Turn on Allow LAN in Clash Verge, or write it in the config:
allow-lan: true
bind-address: "*"
mixed-port: 7897Verify:
netstat -an | findstr 7897Seeing 0.0.0.0:7897 means it worked. 127.0.0.1:7897 means it did not.
Docker: three separate scenarios
Most of the confusion comes from not realising these three need configuring separately.
Scenario 1: docker pull will not fetch images
This is the Docker daemon on the network; containers are not involved.
Docker Desktop (Windows / macOS): Settings → Resources → Proxies → enable Manual proxy configuration and fill in:
HTTP: http://127.0.0.1:7897
HTTPS: http://127.0.0.1:7897
Bypass: localhost,127.0.0.1,*.localDocker Desktop's daemon runs in a VM but can reach the host loopback, so 127.0.0.1 is correct here.
Docker on Linux (systemd):
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/proxy.conf > /dev/null <<'EOF'
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:7897"
Environment="HTTPS_PROXY=http://127.0.0.1:7897"
Environment="NO_PROXY=localhost,127.0.0.1,::1,*.local,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
EOF
sudo systemctl daemon-reload
sudo systemctl restart dockerVerify:
sudo systemctl show --property=Environment dockerScenario 2: dependency installs time out during docker build
Building happens inside a temporary container, so it has to be passed in explicitly:
docker build \
--build-arg HTTP_PROXY=http://host.docker.internal:7897 \
--build-arg HTTPS_PROXY=http://host.docker.internal:7897 \
--build-arg NO_PROXY=localhost,127.0.0.1 \
-t myimage .You do not need to declare these ARGs in the Dockerfile — Docker has built-in support for those particular names.
Scenario 3: the running container needs the network
docker run -it \
-e HTTP_PROXY=http://host.docker.internal:7897 \
-e HTTPS_PROXY=http://host.docker.internal:7897 \
-e NO_PROXY=localhost,127.0.0.1,::1 \
--add-host=host.docker.internal:host-gateway \
ubuntu bashIn docker-compose:
services:
app:
image: myimage
environment:
- HTTP_PROXY=http://host.docker.internal:7897
- HTTPS_PROXY=http://host.docker.internal:7897
- NO_PROXY=localhost,127.0.0.1,::1,app,db
extra_hosts:
- "host.docker.internal:host-gateway"Using the host's LAN IP instead of host.docker.internal
If host.docker.internal is unavailable, use the host's LAN address directly:
# on Windows
ipconfig
# on Linux, the docker0 bridge address (the host as seen from a container)
ip addr show docker0 # usually 172.17.0.1Then substitute that address for host.docker.internal.
The easier route: enable TUN
That is a fair amount of configuration. If you would rather not, turn on TUN mode — all traffic is intercepted at the network layer and neither the Docker daemon nor any container needs configuring.
Under TUN, remember to add Docker's subnets to your direct rules so container-to-container traffic is not proxied:
prepend-rules:
- IP-CIDR,172.17.0.0/16,DIRECT,no-resolve
- IP-CIDR,172.18.0.0/16,DIRECT,no-resolve
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolveWSL2: two networking modes
From a certain version, WSL2 supports mirrored networking, and the two modes are configured entirely differently. Confirm which one you are on first.
# run inside WSL2
cat /etc/resolv.conf
ip route show defaultMode 1: NAT (the default, traditional mode)
WSL2 has its own virtual adapter, and the host appears to it as a gateway address.
# derive the host IP and set the proxy
export HOST_IP=$(ip route show default | awk '{print $3}')
export HTTP_PROXY="http://$HOST_IP:7897"
export HTTPS_PROXY="http://$HOST_IP:7897"
export ALL_PROXY="socks5://$HOST_IP:7897"
export NO_PROXY="localhost,127.0.0.1,::1,*.local"Put it in ~/.bashrc or ~/.zshrc as a pair of switches:
proxyon() {
local host_ip=$(ip route show default | awk '{print $3}')
export HTTP_PROXY="http://${host_ip}:7897"
export HTTPS_PROXY="http://${host_ip}:7897"
export ALL_PROXY="socks5://${host_ip}:7897"
export NO_PROXY="localhost,127.0.0.1,::1,*.local"
echo "proxy on → ${host_ip}:7897"
}
proxyoff() {
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
echo "proxy off"
}Why derive the IP dynamically: WSL2's virtual adapter address can change every time the host reboots, so a hard-coded value stops working.
Mode 2: mirrored networking (newer Windows)
Enable it in %USERPROFILE%\.wslconfig:
[wsl2]
networkingMode=mirrored
dnsTunneling=true
autoProxy=true
firewall=trueRun wsl --shutdown afterwards to restart WSL.
In mirrored mode WSL2 shares the host's network namespace, so 127.0.0.1 works directly:
export HTTP_PROXY="http://127.0.0.1:7897"
export HTTPS_PROXY="http://127.0.0.1:7897"autoProxy=true additionally makes WSL inherit the Windows system proxy setting, so in many cases you need no environment variables at all.
Verifying the proxy works
# check the environment variables
env | grep -i proxy
# try it
curl -I https://www.google.com
# check the exit IP (should be the node's)
curl https://api.ipify.orgIf curl works but apt and pip still do not, those tools have their own proxy configuration; see the developer proxy cheat sheet.
Diagnostic checklist
One quick check, run inside the container or WSL:
curl -v --connect-timeout 5 http://host-ip:7897Connecting at all — even with a 400 response — means the network layer is fine; Connection refused points at allow-lan or the firewall.
In short
- Step one is always allow-lan plus a firewall exception
- Docker's three scenarios each need their own configuration: daemon, build, run
host.docker.internalneeds--add-hoston Linux- For WSL2, first identify the networking mode: NAT needs the gateway IP derived dynamically, mirrored mode takes 127.0.0.1
- Do not leave private ranges and service names out of NO_PROXY
- If it is all too much, enable TUN — but add Docker's subnets to your direct rules
Related: the developer proxy cheat sheet and TUN internals.
Related docs
A reference table worth bookmarking. The proxy command for each tool, where its config file lives, how to undo it, and why some tools ignore your environment variables.
Zero configuration for every device in the house. The trade-offs between three approaches (side router, TProxy, TUN), the full config and systemd service, firewall rules, and fault tolerance so the household stays online.
The complete RESTful API exposed by external-controller: switching nodes, inspecting connections, reloading the config, deploying a web dashboard, and several practical automation scripts.