Development and AI · Clash Technical Blog

Routing Docker and WSL2 Through Clash: Image Pull Timeouts and Network Configuration

Windows, WSL2, the Docker engine, and containers each have their own network environment, so a working browser does not prove docker pull uses the proxy. Configure each layer according to where the request originates.

  • Docker
  • WSL2
  • Development environments
On this page

docker pull, WSL commands, and container requests originate in different places

To route Docker and WSL2 through Clash on Windows, setting a proxy in one terminal is insufficient. docker pull, apt inside WSL, RUN during image builds, and requests from a started container are issued by different components. Configure the layer where the error occurs.

Clash listening on 127.0.0.1 in Windows guarantees access only from Windows itself. WSL2 runs in a NAT virtual network by default, while containers have their own network namespaces. Their 127.0.0.1 normally refers to themselves, not the host.

Docker image pulls, downloads during docker build, and outbound access from running containers are also initiated by different components. Exporting HTTP_PROXY only in a WSL shell may not route Docker Desktop image pulls through the proxy.

Layer where the error occurs

Failed actionComponent actually making the request
curl / apt / git times out inside WSLA process in the WSL distribution
docker pull times outDocker Desktop or Docker Engine daemon
RUN apt-get times out during buildBuildKit / build container
The application request fails after startupRunning container
Proxy paths among Windows, WSL2, and containers
  1. Clash on WindowsListen on a local-network-reachable mixed-port
  2. WSL2Access that port using the host address
  3. Docker buildExplicitly receive HTTP_PROXY and HTTPS_PROXY
  4. Tools inside the containerThen configure Git, npm, or the package manager

127.0.0.1 refers to the current layer in every environment. The proxy address must use a genuinely reachable host address from the preceding layer.

The host proxy port must be reachable from the virtual network

First inspect the actual HTTP or mixed-port in Clash. A common example is 7890, but your client may differ. When WSL accesses it through the host IP, the connection is treated as local-network traffic, so the client must allow LAN access and listen on an address reachable from WSL.

Allowing LAN access expands the port's reachability. Use it only on a trusted private home or work network and have Windows Firewall restrict sources. Do not expose an unauthenticated proxy on public Wi-Fi or the internet. Disable LAN access after testing if you no longer need it.

Default WSL NAT uses the host IP; mirrored networking can try 127.0.0.1 directly

In default NAT mode, WSL can read the next hop of its default route, which is usually the Windows host's address in the WSL virtual network. With mirrored networking enabled on Windows 11 22H2 and later, WSL can access Windows services directly through 127.0.0.1.

Default WSL NAT test; 7890 is the Clash port to replace
HOST_IP=$(ip route show | grep -i default | awk '{ print $3 }')
CLASH_PORT=7890
echo "Windows host: $HOST_IP"
curl -I -x "http://${HOST_IP}:${CLASH_PORT}" https://example.com

Only after this curl receives an HTTP response should you set WSL environment variables. If the proxy port immediately returns Connection refused, the issue is Clash listening, LAN access, or the firewall. If the port is reachable but the external request shows timeout, then inspect the node and rules.

After the port is reachable, set environment variables for the WSL command line

Temporary for the current WSL shell; 7890 is a port placeholder
HOST_IP=$(ip route show | grep -i default | awk '{ print $3 }')
CLASH_PORT=7890
export HTTP_PROXY="http://${HOST_IP}:${CLASH_PORT}"
export HTTPS_PROXY="http://${HOST_IP}:${CLASH_PORT}"
export NO_PROXY="localhost,127.0.0.1,::1"
curl -I https://example.com

Temporary variables disappear when the terminal closes, making them useful for troubleshooting. Repeat the same request with apt, git, or curl; after it succeeds, add the settings to your own startup file according to the shell type.

Supported Windows versions of WSL can also use autoProxy=true in .wslconfig to import Windows HTTP proxy information. Restart WSL after enabling it and inspect the actual variable values.

NO_PROXY should include services available only inside WSL so access to localhost does not detour through the Windows proxy. Add internal corporate domains and private ranges according to the actual network.

If docker pull fails, configure the proxy in Docker Desktop

Docker Desktop image pulls and WSL shell commands originate from different processes. Open Docker Desktop Settings and enter working host HTTP and HTTPS proxies under Resources / Proxies; labels may differ slightly by version. Then apply the settings.

Official Docker documentation explicitly states that Desktop does not read daemon proxy settings from daemon.json. A working WSL terminal does not let you skip Docker Desktop's own proxy settings.

Only when using standalone Linux Docker Engine rather than Docker Desktop should you configure daemon proxies through daemon.json or the systemd environment according to Engine documentation, then restart the Docker service. Identify what you are running first so you do not edit a file that is never read.

WSL curl works, but docker pull still shows timeout

Inspect Docker Desktop Proxies settings and the post-restart status.

Editing daemon.json has no effect in Docker Desktop

Move the setting to the Desktop interface; the product ignores daemon proxy configuration.

Native Linux Engine cannot pull

Inspect the daemon's proxy environment and service logs, not the Windows system proxy.

An image may pull successfully while build steps and running containers still lack a proxy

Docker client configuration can automatically inject proxy variables into new containers and builds or pass them explicitly for a single command. Use --build-arg at build time and --env at runtime. Do not use ENV in a Dockerfile to persist a credential-bearing proxy address; image history and configuration may retain it.

One-time Docker Desktop test; 7890 is the Clash port to replace
CLASH_PROXY=http://host.docker.internal:7890

docker build \
  --build-arg HTTP_PROXY="$CLASH_PROXY" \
  --build-arg HTTPS_PROXY="$CLASH_PROXY" \
  -t demo-app .

docker run --rm \
  --env HTTP_PROXY="$CLASH_PROXY" \
  --env HTTPS_PROXY="$CLASH_PROXY" \
  --env NO_PROXY="localhost,127.0.0.1" \
  demo-app

Docker Desktop normally provides host.docker.internal for reaching the host. Availability in native Linux Engine depends on the environment and may require an explicit host gateway or extra host-gateway mapping. Before running the command, use curl or application logs inside the container to confirm that the hostname and port are reachable.

An incomplete NO_PROXY can send inter-container requests through a distant path

Database names, Compose service names, localhost, and internal ranges normally should not use an external proxy. Add actual internal domains to NO_PROXY so requests to db:5432 or redis:6379 do not detour through Windows. Tools differ in wildcard and CIDR support, so print variables in the target process and make a real request after configuration.

If a proxy address contains a username or password, do not put it in a public compose.yaml, Dockerfile, or image. Use a local environment file or the project's secret-management method, and ensure logs do not print the complete URL.

Complete four validations for actual use

  • curl inside WSL receives a response through Windows Clash
  • docker pull can retrieve a small image
  • Network steps during docker build no longer time out
  • The running container can access the internet and directly reach internal services

The four results correspond separately to WSL, the Docker engine, the build process, and the running container. If one still fails, return to that layer's settings without changing the other three that already passed.

References