Deploy Clash on Linux: Desktop Clients and the mihomo CLI

Install a Linux desktop client or run the mihomo binary as a persistent systemd service on a headless server. Covers config directories and proxy ports.

1. Choose a Setup for Your Environment

There are two common ways to use Clash routing rules on Linux: a desktop client can handle subscriptions, node selection, and the system proxy, while the mihomo CLI reads YAML directly and runs in the background under systemd. mihomo is the name used by the Clash Meta project for its core; it does not require a graphical client.

For everyday browsing on a GNOME or KDE desktop, a graphical client is usually the easiest option. On headless servers, development machines, or always-on hosts, you can run the core directly. Either way, you need a valid configuration—installing a client or binary does not provide proxy nodes.

ComparisonDesktop clientmihomo CLI
Best forGNOME, KDE, and other graphical desktopsHeadless servers and background hosts
ConfigurationConfiguration or subscription settingsExample: /etc/mihomo/config.yaml
Node selectionSelect a node in the proxy groups viewSet the default order in the config or manage it through the API
Run at startupClient startup settingsystemd service
Proxy coverageSystem proxy or TUNStart with a local proxy port; configure TUN if needed

2. Install a Desktop Client and Import a Subscription

Check Your Distribution, CPU Architecture, and Package Format

Run these two commands first. x86_64 generally matches x64 or amd64 packages, while aarch64 generally matches arm64 packages. A package manager won't convert a package for the wrong architecture into a runnable program.

uname -m
cat /etc/os-release

Choose a Linux-compatible client, such as Clash Verge Rev or FlClash, from the Download Center. Ubuntu and Debian typically use DEB packages; Fedora and similar distributions generally use RPM. Check the client's release notes for supported systems and runtime dependencies.

To install a local package, rename the downloaded package for your architecture to client.deb or client.rpm, then place it in your Downloads directory. Choose one of the commands below for your distribution; do not run both.

cd ~/Downloads

# Debian / Ubuntu: run this command only
sudo apt install ./client.deb

# Fedora: run this command only
sudo dnf install ./client.rpm

For an AppImage, you may need to grant execute permission first. Whether it launches can also depend on FUSE and desktop libraries. If it reports a missing shared library, install the dependencies listed by the client. Avoid running the entire graphical application as root.

Follow this sequence: import, enable, select a node, then connect

  1. Launch the client and import the Clash/mihomo configuration URL provided by your service provider from the Configuration or Subscription page. Menu names vary by client. A node share link is not necessarily a complete YAML configuration.
  2. After updating, set the configuration as active. Make sure the node list, proxy groups, and rules have loaded—not just the subscription name.
  3. Open the proxy groups view, select an available node, and run a latency test. A timeout could mean the test address is unreachable or the node is down; check actual connectivity to tell the difference.
  4. Choose a rule mode, then enable the system proxy. Check the local listening port shown in the client; 7890 is just the example used below.

On GNOME, for example, check the manual proxy settings under Settings → Network → Network Proxy. If the client uses mixed-port: 7890, set both HTTP and HTTPS proxies to 127.0.0.1 on port 7890. The settings path varies across desktop environments.

A system proxy does not route traffic from every app. Browsers usually honor desktop proxy settings, but terminal tools, containers, and some standalone apps may not. If the browser works but the terminal doesn't, configure the proxy explicitly for the terminal before switching to a global routing mode.

3. Install the mihomo Core and Set Up Its Config Directory

Prepare the Binary and a Complete YAML Config

For a headless setup, get a mihomo Linux binary that matches your CPU architecture and system. Extract the archive, then rename the executable inside to mihomo. The commands below install it from the current directory and check its version. Keep a note of the version to help diagnose config field compatibility issues.

sudo install -m 0755 ./mihomo /usr/local/bin/mihomo
/usr/local/bin/mihomo -v

This example keeps static configuration separate from runtime data: /etc/mihomo/config.yaml holds the complete config, while /var/lib/mihomo stores writable data such as caches and rule assets. Run the service as a dedicated system user named mihomo rather than giving a regular proxy service administrative access to the whole machine.

getent passwd mihomo >/dev/null || sudo useradd \
  --system --user-group --home-dir /var/lib/mihomo \
  --shell /usr/sbin/nologin mihomo

sudo install -d -m 0750 -o root -g mihomo /etc/mihomo
sudo install -d -m 0750 -o mihomo -g mihomo /var/lib/mihomo

# The config.yaml in the current directory must be a complete, prepared configuration
sudo install -m 0640 -o root -g mihomo \
  ./config.yaml /etc/mihomo/config.yaml

If a user with the same name already exists on the system, check its primary group and directory permissions before proceeding; don't assume the account is set up as described here. Configs may contain node credentials, subscription URLs, and API secrets. Restrict read access to trusted local users and never commit them to a public repository.

Merge the listener settings without removing nodes or rules

Check the following top-level fields in your complete config. This snippet is for merging, not a replacement for the full subscription config. Keep existing entries such as proxies, proxy-providers, proxy-groups, and rules. Do not define the same field more than once in a YAML mapping.

mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
tun:
  enable: false
  • mixed-port accepts both HTTP proxy and SOCKS5 requests on one port. It does not automatically route all system traffic through that port.
  • allow-lan: false and a loopback listener allow local access without exposing a proxy to the public internet.
  • mode: rule routes traffic according to the configured rules. Every policy group referenced by a rule must exist, and the config typically needs an appropriate final fallback rule.
  • Leave TUN disabled for the initial setup so node, routing, DNS, and permission issues don't get tangled together.

Test the config as the service account. -d sets the runtime data directory, -f specifies the config file, and -t tests the config. If it depends on external rules or geodata, make sure those assets are available and their paths are readable. A successful syntax check does not mean nodes can connect.

sudo -u mihomo /usr/local/bin/mihomo \
  -t -d /var/lib/mihomo -f /etc/mihomo/config.yaml

4. Run mihomo as a systemd Service

Once the config passes validation, create /etc/systemd/system/mihomo.service. The unit below is for standard local proxy ports and does not grant the network administration capabilities required by TUN. network-online.target sets a startup dependency; it does not guarantee that nodes or the internet are reachable.

sudo tee /etc/systemd/system/mihomo.service >/dev/null <<'EOF'
[Unit]
Description=mihomo local proxy service
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=mihomo
Group=mihomo
WorkingDirectory=/var/lib/mihomo
ExecStart=/usr/local/bin/mihomo -d /var/lib/mihomo -f /etc/mihomo/config.yaml
Restart=on-failure
RestartSec=5
UMask=0077
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now mihomo
sudo systemctl status mihomo --no-pager
sudo journalctl -u mihomo -b -n 80 --no-pager

active (running) only means the process is still running. Check the logs for config parsing errors, failed asset downloads, or address already in use. If a client is also using port 7890, stop one of them or assign different ports to each process.

After editing config.yaml, test it again before restarting the service. This unit does not define ExecReload, so don't assume systemctl reload mihomo will reload the config. Run daemon-reload only after changing the service file.

sudo -u mihomo /usr/local/bin/mihomo \
  -t -d /var/lib/mihomo -f /etc/mihomo/config.yaml

# Run this only after the previous check succeeds
sudo systemctl restart mihomo
sudo journalctl -u mihomo -b -n 40 --no-pager

5. Check the Port, Terminal Proxy, and Routing Rules

Check the Listener First, Then Send an Explicit Proxy Request

The first command checks which address is listening on TCP port 7890. You should see 127.0.0.1:7890. If it shows a public address or 0.0.0.0, review the listener settings before proceeding. The second command explicitly sends a request to this site through the HTTP proxy. The 20-second timeout is a request limit, not a node latency measurement.

sudo ss -lntp 'sport = :7890'

curl --proxy http://127.0.0.1:7890 \
  --connect-timeout 10 --max-time 20 \
  --output /dev/null --show-error \
  --write-out 'HTTP %{http_code}\n' \
  https://clash-system.com/

An HTTP response confirms that the request reached the local proxy, but it doesn't prove that traffic went through a remote node: the rules may have selected DIRECT. Check the logs for the destination domain, matching rule, and selected policy. If the config uses a proxy group, confirm which node is selected in that group.

Set Proxy Variables for the Current Terminal

For programs that honor proxy environment variables, set these in the current shell. For HTTPS websites, the HTTP proxy URL should still be http://127.0.0.1:7890; the client uses CONNECT to create a tunnel. Don't change the proxy URL scheme to HTTPS just because the destination site uses HTTPS.

export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
export no_proxy=localhost,127.0.0.1,::1

# To test SOCKS5 and resolve hostnames through the proxy
curl --proxy socks5h://127.0.0.1:7890 \
  --connect-timeout 10 --max-time 20 \
  --output /dev/null --show-error \
  https://clash-system.com/

# Restore the current terminal environment
unset http_proxy https_proxy no_proxy

These variables affect only the current shell and its child processes; they are not automatically passed to all systemd services, and sudo may clear them. Inside a container, 127.0.0.1 points to the container itself, not the host. Set up a reachable proxy address and restrict access separately.

6. TUN, DNS, and Remote Access Considerations

TUN Is Optional for Standard Proxy Ports

TUN uses a virtual network device and routing rules to capture traffic, which is useful for apps that ignore system proxy settings. It requires kernel TUN support and involves device access, network administration permissions, and routing configuration. The low-privilege systemd unit above cannot use TUN just by changing tun.enable to true.

  • First check for the device with ls -l /dev/net/tun, then configure service permissions and TUN options according to the current mihomo documentation.
  • Before enabling automatic routing on a remote server, make sure you have console access or another recovery path so you don't lose your SSH connection.
  • When troubleshooting DNS, check the system resolver, mihomo DNS settings, and how TUN handles DNS together to avoid resolution loops or unreachable upstream servers.
  • Don't enable TUN in the desktop client at the same time as automatic routing in another core service.

Hostname resolution works differently across standard HTTP proxies, SOCKS5, and TUN. For example, socks5h sends the destination hostname to the proxy for resolution, so a local getent hosts result can't test every proxy request path. If an IP address works but a hostname doesn't, first identify which DNS path is failing.

Keep the API Separate from the Proxy Port

external-controller is the management API, not the HTTP proxy used for web browsing. A common example is 127.0.0.1:9090, which serves a different purpose from mixed-port: 7890. If you need to enable it, set a separate strong secret and keep it bound to loopback. The example config leaves the API disabled by default.

For remote access, you can forward the local proxy port over SSH. For example, run the command below on your computer to forward local port 17890 to port 7890 on the server. It uses the proxy-server host alias from your SSH config; first set that alias to the real host and username on your computer.

ssh -N -L 127.0.0.1:17890:127.0.0.1:7890 proxy-server

Connect apps on your computer to 127.0.0.1:17890. SSH forwarding does not start the proxy service on the server or change your computer's system proxy. Don't expose port 7890 or the control port directly to the internet for remote access.

7. Update Subscriptions and Troubleshoot Common Issues

Desktop clients usually offer a subscription update option. A standalone mihomo process does not automatically refresh a remote subscription just because it reads a local YAML file. proxy-providers and rule-providers in the config can update their own resources, but that does not replace the full config file.

Before replacing the full config, save a copy of the old file and write the new content to a candidate file. Test it as the same service account. Once it passes, install it as /etc/mihomo/config.yaml and restart the service. Don't overwrite the active config with a single failed network download, and never share subscription URLs in logs or support tickets.

SymptomCheck firstWhat to do
Exec format erroruname -m vs. the downloaded architectureExtract and install the binary for the correct architecture
Permission deniedExecute permission, directory permissions, and service userMake sure the binary is executable, the config is readable, and the runtime data directory is writable
Port 7890 is already in useThe process shown by ssStop the duplicate instance or change the listening port
Process is running, but requests failNode status, rule matches, and DNS logsDistinguish a working local proxy from a successful remote node connection
Browser works, but terminal doesn'tWhether the terminal is using the proxyTest with curl --proxy or proxy environment variables
Config changes after a restartClient subscription updates and the service's -f pathIdentify the config source and avoid editing files that may be overwritten

A reliable deployment check goes in this order: the binary runs, the config passes validation, the service stays up, the loopback port is listening, requests reach the core, and routing rules behave as expected. Verify each step separately before adding automatic updates or TUN to make future issues easier to pinpoint.

Download the Latest Clash Version