Self-hosted service
wnlen/clash-for-linux avatar
wnlen/clash-for-linux

Clash for Linux: what the installer wires up and what it leaves to you

A more complete and elegant Clash/Mihomo proxy platform for Linux.

6,285 stars1,236 forksShellMIT

At a glance

What is it?
Clash for Linux is a shell project that installs, configures, and supervises a Clash or Mihomo core on a Linux machine. Here is how its three layers fit together, which defaults leave your controller reachable from the LAN, and what degrades when you are not root.
Who is it for?
Clash for Linux fits a single Linux box where you want the proxy running after a reboot without hand editing YAML: a VPS, a WSL shell, or an arm64 router that can spare a restart. It does not fit a fleet, a machine where you cannot open port 9090 to the LAN, or anyone who needs a packaged release, because the only tag in the repository is a dated Premium build and installation means cloning master.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 52 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • wnlen/clash-for-linux6,285 โ˜… ยท +234
Star history
Repository2026-09-232026-11-14
wnlen/clash-for-linux6.1k6.3k

234 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Three layers, and a runtime directory that is generated rather than committed

Clash for Linux is a shell project and its layout says so. The tree is short: install.sh, uninstall.sh, scripts/, config/, resources/, .env, and a runtime/ directory the project treats as disposable.

The code splits into three layers. Control is the user entry layer, and it holds the commands you actually type: `clash`, `clashon`, `clashoff`, `status`, `doctor`, `ui`, and `select`. Its job is to fold common actions into commands with readable feedback, which is why `doctor` sits in the same layer as the switch that turns the proxy on. Build is the configuration generation layer: it keeps several subscriptions on disk, marks exactly one of them as the active main subscription, downloads, converts and validates that subscription, applies a run patch from `runtime/mixin.yaml` while still reading `config/mixin.yaml` for compatibility, and writes the result to `runtime/config.yaml`.

Runtime is treated as the single runtime container rather than a configuration directory. It holds the running kernel, which is mihomo or clash depending on configuration, the running config.yaml, subscription state in subscriptions.yaml, the dashboard frontend, logs, and build intermediates under tmp. Everything inside is produced during install or run and is not maintained as repository content.

clash mode flips three routing values, and clashtest exits non-zero when a probe fails

Routing is a small fixed set rather than a free form setting. `clash mode` prints the current Clash routing mode and the same command switches it to `rule`, `global`, or `direct`. Global mode does not choose a node by itself: it uses whatever policy group or node the `GLOBAL` policy group currently points at, so changing it means running `clash select` and picking a policy group and then a node by number.

Connectivity checking is the companion command:

bash
clash mode
clash mode global
clash mode rule
clash mode direct

clash test
clashtest
clashtest "่Š‚็‚น้€‰ๆ‹ฉ"

`clash test` and `clashtest` are equivalent, and the quoted argument tests a named group directly. By default they check whether the currently selected item of the policy group matching the active routing mode can reach Google and YouTube, then print the latency for each. Two details make the command usable from a script: it never switches nodes as a side effect of testing, and it returns a non-zero status when any target is unreachable. For anything deeper than reachability, the diagnostic entry point in the Control layer is `doctor`.

Only the active subscription is compiled, and local files arrive through file:// URLs

Several subscriptions can be stored, but exactly one of them drives the generated config:

bash
clash add <่ฎข้˜…้“พๆŽฅ> <ๅ็งฐ>
clash use
clash ls
clash sub update
clashsub update                 # ๅ…ผๅฎนๆ—ง็‰ˆๅ‘ฝไปค
clash sub
clash sub list
clash sub enable <ๅ็งฐ>
clash sub disable <ๅ็งฐ>
clash sub rename <ๆ—งๅ็งฐ> <ๆ–ฐๅ็งฐ>
clash sub remove <ๅ็งฐ>

`clash add` takes a link and a name, `clash use` picks the active main subscription, `clash ls` lists what is stored, and the `clash sub` group handles update, enable, disable, rename, and remove. `clashsub update` remains as the older spelling of the update step, and the project keeps `clashctl` as a legacy entry point for the same reason.

Local files use an interactive import that reads `$PROJECT_DIR/runtime/subscriptions/`, which is the interactive equivalent of `clash add "file://$PROJECT_DIR/runtime/subscriptions/clash.yaml"`. An absolute `file://` path works too, with an optional name after it. Accepted inputs are Clash or Mihomo YAML, Base64 encoded subscriptions, and share links of the `vmess`, `vless`, `trojan`, `tuic`, `hysteria2`, `hy2`, and `anytls` kinds. Whatever the subscription produces can then be appended to or overridden by the mixin file.

The one line install clones master through an acceleration prefix

There is no package to install and no tagged installer to download. The recommended path clones the master branch shallow and runs the installer:

bash
git clone --branch master --depth 1 https://ghfast.top/https://github.com/wnlen/clash-for-linux.git
cd clash-for-linux
bash install.sh

Two details of that command matter. The clone URL carries `ghfast.top` in front of the repository address, an acceleration service, and the README links `gh-proxy.org` for the prefix and `ghproxy.link` for other usable links if it stops working. Because the command tracks master rather than a tag, the version you get is whatever master held at clone time. The only tagged release in the repository is v1.0.1, named Clash for Linux Premium and dated 2026-04-14, so a release artifact and a fresh clone are not the same thing.

Install options come from a `.env` file or from script parameters, and the README's `.env` example lists kernel type, ports, secret, subscription URL, and version pins with download bases. One environment rule is stated hard: on WSL the project must not sit under a Windows mount such as `/mnt/c/`. Install into a native Linux directory instead.

Without write access to /etc/environment, clashon keeps running but stops persisting

Root is optional, and the failure mode is deliberate rather than fatal. If a WSL or ordinary user session cannot write `/etc/environment`, `clashon` degrades by itself: the proxy starts normally, the variables in the current shell take effect, and persistent system wide takeover plus boot time proxying remain unavailable. The same split is exposed as separate boot commands:

bash
clash boot status
clash boot on
clash boot off
clash boot runtime on|off|status
clash boot proxy on|off|status

`boot runtime` manages autostart of the kernel alone and needs systemd or systemd-user, with the script backend reporting `unsupported`. `boot proxy` manages only the proxy block inside `/etc/environment`, which is what decides whether your proxy variables come back after a restart. `boot` with no subcommand orchestrates both layers at once, so a degraded user session has to keep running `clashon` itself after every reboot.

Where the entry points land also depends on the install mode. A root or system install on OpenWrt writes `clash`, `clashon`, and `clashoff` into `/usr/bin`, while runtime state, logs, and the kernel binary stay inside the project's `runtime/` directory. Tun mode, the transparent proxy takeover path, is the feature that does require a root install.

The controller binds 0.0.0.0 and allow-lan stays true on purpose

The generated run config deliberately keeps LAN access enabled. It writes `allow-lan: true` and binds `external-controller` to `0.0.0.0`, specifically so a subscription that ships `allow-lan: false` cannot quietly switch the project default off. Devices on the same network can then set their HTTP or SOCKS proxy to `http://<LAN IP>:<mixed-port>`, where the mixed port defaults to `7890`. The state is managed with `clash lan status`, `clash lan on`, and `clash lan off`, and a port that refuses connections is usually the firewall rather than the proxy.

That default is why `clashui` prints the exposure instead of a URL alone:

bash
$ clashui
โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘                ๐Ÿฑ Web ๆŽงๅˆถๅฐ                  โ•‘
โ•‘โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
โ•‘                                               โ•‘
โ•‘     ๐Ÿ”“ ๆณจๆ„ๆ”พ่กŒ็ซฏๅฃ๏ผš9090                      โ•‘
โ•‘     ๐Ÿ“ถ ็Šถๆ€๏ผšๅฏ่ฎฟ้—ฎ                            โ•‘
โ•‘     ๐Ÿ  ๅ†…็ฝ‘๏ผšhttp://192.168.0.1:9090/ui       โ•‘
โ•‘     โ˜๏ธ ๅ…ฌๅ…ฑ๏ผšhttp://board.zash.run.place      โ•‘
โ•‘     ๐ŸŒ ๅ…ฌ็ฝ‘๏ผšhttp://8.8.8.8:9090/ui           โ•‘
โ•‘     ๐Ÿ”‘ ๅฏ†้’ฅ๏ผšdada289edb457b59                 โ•‘
โ•‘                                               โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

$ clash

The panel port is 9090 and the console path is `/ui`, with zashboard as the default frontend and `external-ui-url` pointing at its `dist.zip` so the panel's own `/upgrade/ui` can refresh it. Two caveats come with a controller that answers on every interface: the exit IP shown inside the panel is measured by the browser that opened it rather than by the remote proxy, so the real exit has to be checked through the local proxy port on the host; and anyone exposing this to the internet is advised to rotate the key often or reach it over SSH port forwarding. `clash secret` shows the current value and `clash secret 123` sets a new one.

An IPv6 only node needs the kernel, DNS AAAA, and an open UDP path

Mihomo supports Hysteria2, so IPv6 only endpoints can be used, but only when three separate switches line up:

bash
clash config kernel mihomo
clash ipv6 on
clash ipv6 status

The kernel has to be mihomo, and `clash ipv6 on` writes `ipv6: true` and `dns.ipv6: true` together so the resolver is not left behind. `clash ipv6 off` turns both off, while `clash ipv6 auto` keeps whatever the subscription already configured and falls back to compatible defaults when the subscription says nothing about IPv6. The same choice can be pinned as `CLASH_IPV6` set to `true`, `false`, or `auto` in `.env`, followed by `clash config regen` to regenerate the configuration.

The host still needs a usable IPv6 default route, and Hysteria2 travels over UDP and QUIC, so a firewall that only knows about TCP will drop the traffic even when every setting above is correct.

The version pins in `.env` explain where the pieces come from during install: `MIHOMO_VERSION=v1.19.23`, `CLASH_VERSION=v1.18.0`, `YQ_VERSION=v4.52.4`, and `SUBCONVERTER_VERSION=v0.9.9`, each paired with its own `DOWNLOAD_BASE`, alongside `KERNEL_TYPE=mihomo`, `MIXED_PORT=7890`, and `EXTERNAL_CONTROLLER=0.0.0.0:9090`.

OpenWrt support stops at script mode, and MIPS is not promised

The router path is deliberately narrow. It targets x86_64/amd64 and aarch64/arm64 soft routers, reuses the existing `script` run backend, and excludes procd autostart, LuCI, UCI, and opkg packaging. Devices on MIPS or armv7 are not promised either. The project directory itself has to live in persistent storage, since `/tmp` and `/run` disappear on reboot, and the dependency set is installed through opkg:

bash
opkg update
opkg install bash curl tar gzip coreutils-readlink unzip

Installation and manual management use these commands:

bash
bash install.sh
clashon
clash status
clashoff

Since nothing registers a boot service in this mode, `clashon` has to be run again after every restart. Even the root install only writes the command entry points into `/usr/bin`; the kernel binary, logs, and runtime state stay inside the project's `runtime/` directory, so moving or deleting that directory takes the running state with it.

Editorial conclusion

Clash for Linux fits a single Linux box where you want the proxy running after a reboot without hand editing YAML: a VPS, a WSL shell, or an arm64 router that can spare a restart. It does not fit a fleet, a machine where you cannot open port 9090 to the LAN, or anyone who needs a packaged release, because the only tag in the repository is a dated Premium build and installation means cloning master. Before you install, decide whether the subscription should be allowed to set `allow-lan`, check that the host has a usable IPv6 default route if you plan to use IPv6 only nodes, and pick a secret you are willing to keep.

Frequently asked questions

What does Clash for Linux do on a Linux machine?

It is a shell script platform that installs the Clash or Mihomo core for your architecture, generates the run config from one active subscription, and exposes commands such as `clashon`, `clashoff`, `clash use`, `clash mode`, `clashtest`, `clash lan`, and `clashui` for day to day control.

Does Clash for Linux need root to work?

Not for the basic path. When a WSL or ordinary user session cannot write `/etc/environment`, `clashon` still starts the proxy and applies the variables to the current shell, while persistent takeover and boot time proxying stay unavailable. Tun mode does require a root install.

Where does Clash for Linux keep its files at runtime?

The `runtime/` directory is the single runtime container: the running kernel, `config.yaml`, `subscriptions.yaml`, the dashboard frontend, logs, and build intermediates under `tmp`. All of it is generated during install or run rather than committed to the repository.

How do I switch subscriptions in Clash for Linux?

Add each one with `clash add` and a link plus a name, then run `clash use` to choose the active main subscription. Only the active one is compiled into `runtime/config.yaml`, and `clash sub` covers update, enable, disable, rename, and remove.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. wnlen/clash-for-linux on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wnlen-clash-for-linux.svg)](https://hysenlabs.com/projects/wnlen-clash-for-linux)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/wnlen-clash-for-linux.svg)](https://hysenlabs.com/projects/wnlen-clash-for-linux)

Was this useful?