Why developers need a deliberate Clash workflow
GitHub timeouts, failed npm install commands, slow Docker pulls, and AI coding tools that stop halfway through a request often look like unrelated problems. In practice, they can share one networking cause: the browser uses a proxy, while terminal processes, container engines, and background developer tools still attempt a DIRECT connection. A page may open normally in Chrome while git clone hangs, npm cannot resolve a registry, or Docker reports that a manifest cannot be fetched.
This guide presents a practical Clash developer setup built around Mihomo-compatible clients such as Clash Verge Rev, Clash Verge, or another desktop client that supports TUN mode. The goal is not to send every packet through one permanent tunnel. Instead, you will create an observable routing path for development traffic, use terminal variables where applications need them, keep local services on NO_PROXY or DIRECT paths, and verify each layer with logs rather than guessing.
The distinction matters because a developer workstation has several networking planes. Your browser may respect the system proxy. Git can read its own configuration and environment variables. Node.js tools may use lowercase proxy variables, a package-manager setting, or no proxy at all. Docker has a daemon-side network context that is separate from the shell where you type the command. An IDE extension may launch a child process with a filtered environment. TUN mode can cover many of these gaps, but it also changes DNS behavior, local-network access, and the way you diagnose failures.
Use only endpoints and proxy services that you are authorized to use, and keep credentials out of shell history, public configuration files, screenshots, and container images. A reliable developer setup should improve observability and repeatability, not hide an unsafe secret-management practice.
What TUN mode changes on a development machine
A regular system proxy usually handles applications that explicitly understand HTTP, HTTPS, or SOCKS proxy settings. That is convenient for browsers and some desktop applications, but many command-line binaries do not automatically inherit the setting. A process may open a socket directly, resolve a hostname through the operating system, or use a protocol that the application does not associate with the configured proxy.
TUN mode creates a virtual network interface. Mihomo can inspect traffic entering that interface, apply Clash rules, and forward selected connections through a proxy group. From a developer’s perspective, this provides a wider safety net for tools that ignore the system proxy. It is particularly useful for applications that create their own networking stack, GUI-based IDE integrations, language servers, and helper processes launched outside the terminal that you configured.
TUN is not a magic replacement for application configuration. A tool may still fail because it pins certificates, uses an unsupported protocol, relies on a Unix socket, or runs inside a separate virtual machine. Docker Desktop, WSL2, remote SSH sessions, and dev containers can each have a different network namespace. Treat TUN as one routing layer in a chain rather than assuming that a green TUN switch proves every process is covered.
| Traffic source | Typical control point | What to verify |
|---|---|---|
| Browser and desktop apps | System proxy or TUN | Connection appears in Mihomo logs with the expected rule |
| Git, curl, npm, pnpm | Environment variables, tool settings, or TUN | Hostname, port, and proxy inheritance are consistent |
| Docker CLI | Docker daemon or Docker Desktop proxy settings | Pull and build traffic originates from the daemon context |
| SSH and remote development | SSH proxy command, SOCKS forwarding, or remote-side proxy | Both the local handshake and remote outbound requests are understood |
Enable TUN without losing local access
Open the TUN or enhanced-network settings in your Clash client and read the permission prompt carefully. Administrative privileges are normal because a virtual interface and routing rules require system access, but the component name and application identity should match the client you installed. Disable other full-tunnel VPNs during the first test. Two programs competing for the default route can produce misleading symptoms, including intermittent DNS failures and connections that disappear from the Clash dashboard.
Start with rule mode rather than Global mode. Confirm that the client has a working proxy group, a reachable DNS configuration, and a profile that contains rules for the services you actually use. If your client exposes an option for auto-route, strict route, or system interface detection, enable only the behavior you understand. On a laptop that frequently changes between home Wi-Fi, office Ethernet, and a phone hotspot, interface detection can matter more than a hard-coded adapter name.
Keep local development traffic local. Addresses such as 127.0.0.1, localhost, private LAN ranges, local Kubernetes endpoints, and internal package registries should not be forced through a remote node unless your network design specifically requires it. Test a local web server, a database connection, and an ordinary public HTTPS request after enabling TUN. If localhost breaks, inspect the client’s bypass or DNS settings before changing unrelated rules.
Build a clean terminal proxy environment
Environment variables are useful because they make a command’s behavior explicit and portable across many CLI tools. The common variables are HTTP_PROXY, HTTPS_PROXY, and ALL_PROXY, with lowercase copies added for programs that check Unix-style names. A local Clash HTTP or mixed port is often suitable for HTTP-based package managers; a SOCKS port may be more appropriate for applications that support SOCKS directly. Use the port displayed by your client instead of copying a number from an old tutorial.
For a temporary test, export the variables only in the current shell. This makes it easy to compare DIRECT and proxied behavior without permanently changing every terminal session:
export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
export ALL_PROXY=socks5://127.0.0.1:7891
export http_proxy="$HTTP_PROXY"
export https_proxy="$HTTPS_PROXY"
export all_proxy="$ALL_PROXY"
export NO_PROXY=localhost,127.0.0.1,::1,.local
export no_proxy="$NO_PROXY"
Do not blindly set every variable to the same protocol. Some clients expect an HTTP proxy URL even when the destination is HTTPS, because the client sends a CONNECT request to the proxy. Other tools interpret ALL_PROXY as a SOCKS endpoint. Read the tool’s documentation and validate the result with a harmless request. Also remember that NO_PROXY matching differs between implementations. A leading dot may match subdomains in one program and behave differently in another, so test the exact internal hostname used by your team.
Git has its own configuration layer. You can inspect existing settings before adding anything:
git config --global --get-regexp 'http\..*proxy|https\..*proxy'
git config --show-origin --get-regexp 'http\..*proxy|https\..*proxy'
git ls-remote https://github.com/example/project.git
If a stale Git proxy is configured, it can override your current environment and make diagnosis confusing. Remove obsolete entries rather than stacking several values:
git config --global --unset http.proxy
git config --global --unset https.proxy
For SSH-based Git remotes, HTTPS variables do not control the connection. A GitHub URL beginning with git@ uses SSH, so configure SSH separately. One common pattern is to use a local SOCKS-aware helper, provided your client and operating system support it:
Host github.com
HostName github.com
User git
ProxyCommand connect -S 127.0.0.1:7891 %h %p
Do not assume that this example is valid for every platform or helper binary. Verify the helper’s syntax, keep host-specific rules narrow, and run ssh -T [email protected] while watching the Clash Connections panel. If HTTPS cloning works but SSH fails, that contrast points to SSH policy or helper configuration, not necessarily a broken node.
Make npm, pnpm, and language tools predictable
Package managers add another layer because they may use their own configuration files, respect environment variables selectively, or invoke secondary tools during installation. An npm install can download the main package successfully and then fail while fetching a Git dependency, a prebuilt binary, or an optional native module from another host. Diagnose the complete request chain instead of adding a rule for only the registry hostname shown in the first error.
Inspect npm’s effective settings and avoid storing a proxy password in a shared project file:
npm config get registry
npm config get proxy
npm config get https-proxy
npm config list
For a short-lived test, set the registry and proxy in the command environment or user-level configuration, depending on your team’s policy. Keep the public registry choice intentional. A project can use an internal mirror for approved dependencies while sending a specific external service through Clash. That is usually safer and faster than routing every dependency request through a distant node.
pnpm and Yarn can behave differently from npm, particularly when a project uses Plug’n’Play, a custom fetcher, or a lockfile containing Git URLs. Check the package manager’s documentation for the version installed in the repository. When a dependency fails, record the exact hostname, protocol, HTTP status, and whether the failure occurs during DNS lookup, TLS negotiation, download, or extraction. Mihomo’s log entry helps distinguish a rule miss from a remote registry error.
Native modules deserve special attention. Packages may download binaries from release hosting, object storage, or vendor-specific CDN domains. A rule that covers the package registry does not automatically cover those download endpoints. Prefer domain rules based on observed traffic, then test a clean install in a disposable directory. Avoid adding broad wildcard rules merely because they make one installation pass; overly broad proxying can slow internal mirrors and expose unrelated traffic to a remote path.
When the terminal is configured correctly but an IDE installation still fails, compare the environment visible to the IDE and to your shell. GUI applications launched from a desktop menu may not read shell startup files. TUN mode can cover the missing route, while explicit IDE proxy settings may be needed for extension marketplaces and language servers. Change one layer at a time and keep a written record of the effective configuration.
Separate Docker client, daemon, and container traffic
Docker failures are frequently misdiagnosed because the command is typed in one environment while the network request is performed by another. With Docker Desktop, the engine runs inside a managed Linux environment. With a remote Docker context, the daemon may be on a server across the network. Exporting HTTPS_PROXY in your local shell can help the Docker CLI contact the daemon, but it does not necessarily configure the daemon to pull images from a registry.
First identify the active context and engine:
docker context show
docker context ls
docker info
docker version
If image pulls fail, inspect the daemon or Docker Desktop proxy configuration. Configure the proxy in the location used by that engine, restart the daemon when required, and then test with a small public image or an approved internal image. Do not place proxy credentials in a Dockerfile. Build arguments can leak through image history, logs, or cache metadata, and a credential embedded in a container layer is difficult to revoke.
Build traffic has additional sources. BuildKit may contact registries for base images, fetch remote Git contexts, download language dependencies, and execute package-manager commands inside a build step. A host-side TUN route does not guarantee that every build process shares the host network path. If builds run in a VM or remote builder, configure that builder’s network policy. If builds run inside a container, pass only the necessary proxy variables and use a carefully scoped NO_PROXY for internal services.
These variables are commonly passed during a controlled build test:
docker build \
--build-arg HTTP_PROXY=http://host.docker.internal:7890 \
--build-arg HTTPS_PROXY=http://host.docker.internal:7890 \
--build-arg NO_PROXY=localhost,127.0.0.1,.internal \
-t developer-proxy-test .
The hostname used to reach the host differs across operating systems and Docker modes, so treat host.docker.internal as a test value, not a universal guarantee. On Linux, it may require an explicit host gateway mapping. Also distinguish build-time variables from runtime variables: a successful image build does not mean the running application can reach its API, and a runtime proxy setting does not fix a base-image pull performed by the daemon.
For teams, a registry mirror or an approved artifact repository is often more sustainable than routing every Docker layer through a personal desktop client. Clash is valuable for testing and controlled development egress, while organization-wide image distribution should follow the company’s security, caching, and audit requirements.
Use targeted rules and a repeatable debugging loop
Rule design should follow observed traffic and project boundaries. Put specific developer services above broad catch-all rules, and keep local domains and private networks explicit. Depending on the profile format and client version, you may use domain, suffix, keyword, IP-CIDR, or process-based matching. The exact syntax can vary, so validate the profile in the client before relying on it for a workday.
A useful conceptual order is:
- Local bypass first: localhost, private service names, LAN databases, internal Git, and local package mirrors should use DIRECT when policy allows.
- Approved developer services next: source hosting, package registries, container registries, documentation APIs, and AI coding endpoints should use a deliberate proxy group.
- General traffic afterward: leave ordinary destinations to the profile’s normal rule set instead of expanding a developer exception into a global tunnel.
- Final fallback last: make the final rule intentional and easy to identify in logs.
During an incident, reproduce one small operation at a time. Start with DNS resolution, then a TLS request, then the actual tool:
curl -I https://github.com
curl -I https://registry.npmjs.org
git ls-remote https://github.com/example/project.git
npm view lodash version
docker pull hello-world
Watch Mihomo’s Connections or Logs panel while each command runs. Confirm the destination hostname, selected rule, proxy group, connection protocol, and outcome. If no connection appears, the application may be using another network namespace, a cached result, a Unix socket, or a direct path that the current TUN configuration does not intercept. If the connection appears as DIRECT, inspect rule order and DNS classification. If it uses the intended group but resets repeatedly, test another node and compare latency, packet loss, and long-lived connection stability.
DNS deserves separate attention. Fake-IP or redirection modes can improve policy matching, but some developer tools expect literal address behavior, perform their own DNS lookup, or reject unusual responses. Internal split DNS can also be damaged when every query is sent to a remote resolver. Keep internal names on the resolver that knows them, and avoid publishing sensitive corporate hostnames to an external DNS service. When a hostname resolves differently in the shell, browser, and container, record where each lookup occurs before editing rules.
Finally, remove temporary workarounds after the root cause is known. A forgotten Global mode, an old Git proxy, duplicated lowercase variables, or a broad NO_PROXY=* can silently change behavior weeks later. Save a small diagnostic note containing the client version, profile revision, active Docker context, effective proxy variables with secrets redacted, and the command that reproduced the failure. That record turns the next timeout into an engineering task instead of a session-wide mystery.
Cover AI coding tools without hiding security boundaries
AI coding assistants add long-lived streaming requests, authentication redirects, telemetry endpoints, file-upload hosts, and sometimes separate CLI processes. A browser-based chat working through Clash does not prove that an AI extension or coding agent has the same route. Inspect the Connections panel while starting a request and identify the hostnames actually used by the tool. Vendor infrastructure changes, so treat current logs and official documentation as more reliable than a copied domain list.
Export proxy variables in the process that launches the tool, not merely in a terminal you opened earlier. If an IDE was started before the variables were set, restart it or configure its documented proxy field. For a service manager, CI runner, WSL distribution, or dev container, define the environment in that execution context and protect credentials through the platform’s secret store. Never commit a token-bearing proxy URL to .env.example, a repository script, or a shared shell profile.
Use NO_PROXY for local model servers and internal endpoints when appropriate. This prevents a request to a local Ollama-style service, a private API gateway, or a company resource from taking an unnecessary external route. At the same time, do not use an overly broad bypass simply to silence one error. A broad bypass can send model API traffic DIRECT and recreate the original timeout while making the logs appear clean.
There is also a trust decision around TLS inspection. Most developer tools rely on the operating system or language runtime trust store. If a corporate security product intercepts TLS, the required CA certificate must be installed through the approved enterprise process. Disabling certificate verification is not a safe proxy fix. Flags such as insecure TLS options can expose source code, package downloads, credentials, and model prompts to interception, and they may make a test appear successful while leaving production behavior undefined.
Compared with single-purpose VPN clients that offer little visibility into which process was routed, Clash gives developers a useful combination of TUN coverage, rule ordering, proxy groups, per-connection logs, and explicit terminal integration. Some GUI-only alternatives simplify the first click but become difficult when Git, npm, Docker, SSH, and an IDE each need different handling; a system-wide tunnel can also slow local registries or hide the distinction between a routing error and a vendor outage. If you want one observable workflow that can switch between targeted rules and broader TUN coverage while keeping local development traffic under control, choose the Clash client build that matches your platform and continue with a measured, testable configuration.