Development and AI · Clash Technical Blog

How to Route VS Code Dev Containers Through Clash

Dev Container builds, Git inside the container, and extension downloads may come from different processes. Check the host proxy address, build variables, or container environment according to the error location, set NO_PROXY for internal access, and verify downloads and runtime separately.

  • VS Code
  • Dev Containers
  • Docker
  • Proxy
On this page

The host browser may work while you get stuck pulling images, installing dependencies, or downloading extensions for a development container. If container proxy settings do not help, they may not apply to the program making the request. Identify where the failed command runs, check whether it can reach the host proxy, and keep databases and other internal services on direct connections.

Where do Dev Containers stall: image pulls, builds or downloads inside the container?

Record the failed command and where it runs, using Dev Containers output or the container terminal. Distinguish base-image pulls, Dockerfile build steps, Git inside the container, and remote-extension downloads. This VS Code feature places the development environment in a container, but requests come from different processes. Use the table below to configure the proxy where the failure actually occurs.

Proxy settings for different download operations

Failed actionComponent making the requestConfiguration entry point
docker pull / pull a base imageDocker daemonDocker Desktop or daemon proxy
Dockerfile RUN apt/npmImage build stagebuild args or BuildKit configuration
curl/Git/pip inside the containerRunning containercontainerEnv, remoteEnv, or tool-specific configuration
VS Code remote extension downloadLocal VS Code and the container-side serviceLocal and container network settings, plus the extension’s own configuration

If the error appears before “Starting Dev Container,” containerEnv in .devcontainer/devcontainer.json usually cannot fix it because the container is not running yet. Record the failed command and its stage in the VS Code output panel first.

The four Dev Containers network stages
  1. VS Code hostPulls extensions and starts Docker
  2. Docker build stageReads build args or Docker proxy settings
  3. Running containerReads containerEnv
  4. Remote extensions and terminalReads remoteEnv or the tool's own configuration

Identify whether the download fails on the host, during the build, or inside the running container, then configure the proxy for that environment. These environments do not automatically share environment variables.

127.0.0.1 inside the container is not the host machine

When Clash runs on the host, http://127.0.0.1:7890 inside a container points to the container itself. Docker Desktop provides host.docker.internal on Windows and macOS; Linux Docker can map the same name through host-gateway.

If Clash listens only on the host loopback address, the container may still be unable to connect. When enabling local-network access or binding mixed-port to an interface the container can reach, also restrict sources with the system firewall. Never expose 7890 directly to the public internet.

Run the first part below in the terminal of an existing container. The final docker run command is for the Linux host terminal and creates a temporary test container; do not repeat it inside the development container. Replace both occurrences of 7890 with the client’s actual port.

Verify the port from inside the container first; change CLASH_PORT to the client's actual mixed-port
CLASH_HOST=host.docker.internal
CLASH_PORT=7890
getent hosts "$CLASH_HOST" || true
curl -v -x "http://$CLASH_HOST:$CLASH_PORT" https://www.example.com/

# Linux Docker 临时测试
docker run --rm --add-host=host.docker.internal:host-gateway curlimages/curl:latest -v -x http://host.docker.internal:7890 https://www.example.com/

On a Linux host, add host-gateway to devcontainer.json

.devcontainer/devcontainer.json snippet
{
  "runArgs": [
    "--add-host=host.docker.internal:host-gateway"
  ]
}

After editing, run Dev Containers: Rebuild Container. An existing container will not automatically receive the new hosts mapping.

For a Docker Compose project, use extra_hosts: ["host.docker.internal:host-gateway"] under the service. Do not add it in both places.

Set containerEnv and remoteEnv once the container can reach the host port

Set tool environments after curl inside the container receives an HTTP response through the proxy. The example below is for Dockerfile or image projects and supplies both uppercase and lowercase variables for tool compatibility. Adjust the mixed-port. In Compose projects, put container variables in the service’s environment and keep only remoteEnv in devcontainer.json.

Project-level devcontainer.json example
{
  "containerEnv": {
    "HTTP_PROXY": "http://host.docker.internal:7890",
    "HTTPS_PROXY": "http://host.docker.internal:7890",
    "NO_PROXY": "localhost,127.0.0.1,::1,host.docker.internal",
    "http_proxy": "http://host.docker.internal:7890",
    "https_proxy": "http://host.docker.internal:7890",
    "no_proxy": "localhost,127.0.0.1,::1,host.docker.internal"
  },
  "remoteEnv": {
    "HTTP_PROXY": "${containerEnv:HTTP_PROXY}",
    "HTTPS_PROXY": "${containerEnv:HTTPS_PROXY}",
    "NO_PROXY": "${containerEnv:NO_PROXY}",
    "http_proxy": "${containerEnv:http_proxy}",
    "https_proxy": "${containerEnv:https_proxy}",
    "no_proxy": "${containerEnv:no_proxy}"
  }
}

Using http:// at the start of HTTPS_PROXY is usually correct: it means reaching HTTPS through an HTTP CONNECT proxy, not turning the proxy port itself into HTTPS. Add Compose service names, the corporate intranet, and local development domains to NO_PROXY as well; otherwise, container requests to a database or API may be routed through Clash.

After saving, run Rebuild Container, then execute env | grep -i proxy in a new terminal. Existing terminal processes do not automatically refresh remoteEnv.

Image pulls and Dockerfile builds require separate configuration

Configure image pulls on the Docker Desktop or builder/daemon side; Dockerfile RUN commands use build.args. On Linux, the earlier runArgs host mapping affects only the running container, so the build also needs the build.options mapping below. Do not leave personal proxies or credentials in the image through ENV.

devcontainer build-argument snippet; change 7890 to the actual mixed-port
{
  "build": {
    "dockerfile": "Dockerfile",
    "args": {
      "HTTP_PROXY": "http://host.docker.internal:7890",
      "HTTPS_PROXY": "http://host.docker.internal:7890",
      "NO_PROXY": "localhost,127.0.0.1",
      "http_proxy": "http://host.docker.internal:7890",
      "https_proxy": "http://host.docker.internal:7890",
      "no_proxy": "localhost,127.0.0.1"
    },
    "options": [
      "--add-host=host.docker.internal:host-gateway"
    ]
  }
}

After curl succeeds, test Git, apt, and extensions separately

In the container terminal, confirm that HTTP_PROXY, HTTPS_PROXY, and NO_PROXY are present, then run Git and package-manager commands separately. Git normally reads the environment variables. If it still uses an old address, inspect Git's own configuration.

When installing extensions through the Extensions view or devcontainer.json, both the local machine and the container-side VS Code Server need access to the Marketplace and download CDN. After changing remoteEnv, reconnect and open a new terminal, then check the Output panel for the request that actually fails. Changing remoteEnv alone does not require rebuilding the entire container.

Check Git inside the container; replace REPO_URL with your project's public repository
REPO_URL='https://github.com/octocat/Hello-World.git'
env | grep -i '_proxy'
git config --show-origin --get-regexp 'http.*proxy' || true
git ls-remote "$REPO_URL"

# 单次指定代理,不修改已有全局设置
git -c http.proxy=http://host.docker.internal:7890 ls-remote "$REPO_URL"

curl works, but Git still connects to the old port

Run git config --show-origin --get-regexp 'http.*proxy' and remove stale user or repository settings.

apt update fails

Inspect the actual repository domain, certificate, and any independent proxy under /etc/apt/apt.conf.d.

The project runs, but remote extensions will not install

Find the marketplace request in the Dev Containers log and confirm that the remote service inherited remoteEnv.

The container has internet access, but the local database connection fails

Add the database service name and private address range to NO_PROXY.

The Rebuild stage still fails

Return to daemon image pulls or Dockerfile build args; do not keep editing the running container.

Do not hard-code a personal proxy in a shared repository

For team projects, devcontainer.json can read each developer's local environment with ${localEnv:HTTP_PROXY}, or you can provide an .env.example without a real address.

Document in README that HTTP_PROXY must be a complete address, such as http://host.docker.internal:7890. Before committing, confirm that the proxy URL contains no username, password, or token.

Before sharing the project, verify docker pull, Rebuild Container, Git/package-manager commands inside the container and access to local services separately. Save the failed command and its logs, then check the corresponding configuration entry in this article. There is no need to change every proxy setting at once.

References