Choose your setup: desktop session or background service
mihomo is the core engine used by the Clash Meta project. It loads configuration, matches rules, and establishes connections; a graphical client handles subscriptions, tray menus, and system integration. Installing the core binary alone won't open a desktop window or automatically route all Linux applications through a proxy.
If you use a desktop environment such as GNOME or KDE and regularly switch nodes, choose a client that bundles the mihomo core. For servers managed over SSH, or when the process needs to keep running after you close the terminal, deploy the core separately and manage it with systemd. Both approaches can use similar YAML configuration, but don't bind them to the same ports at the same time.
| Category | Desktop client | Standalone core with systemd |
|---|---|---|
| Environment | Graphical desktop and user session | systemd-based host or virtual machine |
| Configuration interface | Subscription, configuration, and connection UI | YAML files, logs, and control API |
| Startup | Starts after desktop login, or through a client service | Starts at boot as a system service |
| Scope of this guide | Start with the system proxy | Start by exposing a local proxy on port 7890 |
Option 1: Install a Linux desktop client
Check your CPU architecture and package format
Run these two commands in a terminal first. x86_64 usually corresponds to x64 or amd64 downloads, while aarch64 corresponds to arm64. CPU architecture and distribution package format are separate requirements: choosing a DEB package doesn't guarantee it contains the right CPU build.
uname -m
cat /etc/os-release
- Debian and Ubuntu: Choose a DEB package compatible with your distribution.
- Fedora: Choose an RPM package for your distribution and install dependencies with the package manager.
- AppImage: Requires execute permission; whether it runs directly also depends on system libraries and FUSE support.
Choose a Linux installer from the client downloads page. The commands below assume you've renamed a local DEB package to clash-client.deb and changed into its directory. This is an example, not an actual release filename.
sudo apt install ./clash-client.deb
Verify the subscription before enabling the system proxy
- Launch the client and import the provider's Clash YAML subscription from its subscription or configuration management screen. Menu names vary by client, so don't assume one app's menu path applies to every client.
- Select the imported configuration and check the core startup logs. A populated node list doesn't guarantee that rules, proxy groups, and DNS settings are all valid.
- Select Rule mode, choose an available node in a proxy group, and note the local HTTP or mixed port shown by the client.
- Turn on the client's system proxy setting, then visit a site in your browser. Check the connection list too, to confirm traffic is using the expected proxy group.
For example, in GNOME, go to Settings → Network → Network Proxy and select Manual. Set the HTTP and HTTPS proxy to 127.0.0.1 and the port shown by your client. This connects to a local HTTP proxy; HTTPS sites usually use a CONNECT tunnel, so don't change the proxy address to HTTPS.
The system proxy mainly serves applications that respect the desktop proxy settings. Terminal programs, containers, and some games may ignore them. For those, configure a proxy explicitly or set it up per application; an enabled system proxy does not mean all device traffic is routed through it.
Option 2: Set up the mihomo binary and configuration directory
Get a Linux core build that matches your CPU architecture and instruction set from the project release channel. Extract it and name the executable mihomo. Run the commands below from the directory containing the file. For older x86 CPUs, check the build's instruction-set requirements; “64-bit” alone doesn't guarantee compatibility.
chmod +x ./mihomo
./mihomo -v
sudo install -m 0755 ./mihomo /usr/local/bin/mihomo
sudo useradd --system --user-group \
--home-dir /var/lib/mihomo --shell /usr/sbin/nologin mihomo
sudo install -d -o root -g mihomo -m 0750 /etc/mihomo
sudo install -d -o mihomo -g mihomo -m 0750 /var/lib/mihomo
If the mihomo system account already exists, skip account creation and check its group membership. The nologin path varies by distribution; run command -v nologin to find it. Keep the configuration directory under root's control and grant the service account write access only to its runtime directory, so the network service can't modify its own full configuration.
Start with a minimal configuration to verify the service
This direct-connection configuration is suitable for checking that the core starts. It has no proxy nodes, and the final rule sends all requests directly, so it can't test remote proxy access. Verify the process, directory permissions, and listening port first; then switch to a complete configuration from a trusted source. This makes troubleshooting easier.
sudo tee /etc/mihomo/config.yaml > /dev/null <<'YAML'
mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
ipv6: false
rules:
- MATCH,DIRECT
YAML
sudo chown root:mihomo /etc/mihomo/config.yaml
sudo chmod 0640 /etc/mihomo/config.yaml
sudo -u mihomo /usr/local/bin/mihomo -t \
-d /var/lib/mihomo -f /etc/mihomo/config.yaml
mixed-port: 7890 accepts both HTTP and SOCKS proxy connections on the same port. -d sets the working directory, where runtime files such as caches are usually stored; -f explicitly specifies the main configuration file. A successful configuration check means the core can parse and load the current configuration, not that every node is reachable.
After replacing the minimal file with a complete subscription configuration, run -t again. Some configurations reference GeoIP, GeoSite, or remote rule sets, which may require additional files on the first check. If the server can't reach those URLs yet, provide the required resources in advance or test with a configuration that doesn't depend on them to isolate startup issues.
Create a systemd service for startup, logs, and recovery
Save the following as /etc/systemd/system/mihomo.service. The service runs under a dedicated account and exposes a standard proxy port only; it doesn't have the network administration privileges needed to create a TUN interface. Don't run it as root without first understanding the permission implications.
[Unit]
Description=mihomo local proxy service
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=mihomo
Group=mihomo
StateDirectory=mihomo
StateDirectoryMode=0750
WorkingDirectory=/var/lib/mihomo
ExecStartPre=/usr/local/bin/mihomo -t -d /var/lib/mihomo -f /etc/mihomo/config.yaml
ExecStart=/usr/local/bin/mihomo -d /var/lib/mihomo -f /etc/mihomo/config.yaml
Restart=on-failure
RestartSec=5s
UMask=0027
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/lib/mihomo
[Install]
WantedBy=multi-user.target
StateDirectory manages the runtime data directory, ProtectSystem=strict restricts changes to system files, and ReadWritePaths preserves write access to the runtime directory. If your configuration references certificates or rule files in a home directory, ProtectHome=true will block access. Move required files into a dedicated directory and grant read-only access instead of disabling all protections.
sudo systemctl daemon-reload
sudo systemctl enable --now mihomo
systemctl status mihomo --no-pager
sudo journalctl -u mihomo -b -n 80 --no-pager
sudo ss -lntp 'sport = :7890'
Under normal conditions, the service should show active (running), with port 7890 listening locally. network-online.target sets startup ordering; it doesn't guarantee the subscription server is reachable at that moment, so check the logs if network issues occur. If repeated startup failures trigger systemd's rate limit, fix the problem, then run sudo systemctl reset-failed mihomo before starting the service again.
Follow this sequence after changing the configuration
- Keep a backup of the old configuration, then edit
/etc/mihomo/config.yaml. Don't put files containing credentials in a public directory. - Check the configuration using the same account and working directory as the service, with the same
-fargument. - Once the check passes, run
sudo systemctl restart mihomo, then check the logs and listening port.
Changing only the YAML file doesn't require daemon-reload; changing the service unit does. This guide doesn't configure ExecReload, so don't rely on systemctl reload mihomo to apply updates.
Check proxy coverage: terminal, rules, and TUN
Starting the service doesn't set environment variables in your current shell. Explicitly route a single request through the proxy first to distinguish “the app isn't using the proxy” from “the core can't forward traffic.” The request below checks the connection and HTTP response; it isn't a latency or speed benchmark.
curl -I --connect-timeout 10 \
--proxy http://127.0.0.1:7890 https://example.com
curl -I --connect-timeout 10 \
--proxy socks5h://127.0.0.1:7890 https://example.com
socks5h sends the destination hostname to the proxy for resolution, while socks5 usually resolves it on the client first. The two options can use different DNS paths. With the minimal configuration, requests still go direct; after switching to a configuration with nodes and proxy groups, check connection records to confirm which rule matched and which exit was used.
To use the proxy in terminal programs that support these variables, set them temporarily:
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
export all_proxy=socks5h://127.0.0.1:7890
export no_proxy=localhost,127.0.0.1,::1
# Remove these proxy variables from the current shell only
unset http_proxy https_proxy all_proxy no_proxy
Not all programs read the same variables, and systemd services don't automatically inherit export settings from an interactive terminal. If another background service needs a proxy, configure its environment separately and consider which local addresses should be excluded.
Connect a Web dashboard: bind the control port to loopback
A Web dashboard is a frontend for the control API, not the proxy core. Port 7890 forwards proxy traffic; in the example below, port 9090 manages the core. They serve different purposes, so don't enter 9090 as your browser's HTTP proxy port.
Generate a control secret and enable the API
The commands below require OpenSSL and assume the current configuration has no existing external-controller or secret fields. They append a listening address and a randomly generated secret to the end of the file. If either field already exists, edit it in place to avoid duplicate keys.
sudo sh -c '
set -eu
key=$(openssl rand -hex 24)
printf "\nexternal-controller: 127.0.0.1:9090\nsecret: \"%s\"\n" "$key" >> /etc/mihomo/config.yaml
'
sudo -u mihomo /usr/local/bin/mihomo -t \
-d /var/lib/mihomo -f /etc/mihomo/config.yaml
sudo systemctl restart mihomo
sudo ss -lntp 'sport = :9090'
The secret is an administrative credential; don't share it in public logs or screenshots. Enter http://127.0.0.1:9090 and the newly generated secret in the dashboard connection settings. Keep authentication enabled even when binding to loopback, since other local processes on the host may also be able to access the port.
Serve a local static dashboard
Get a static dashboard build that's compatible with the current core API, extract it to /var/lib/mihomo/ui, and make sure index.html is directly inside that directory and readable by the mihomo account. Then add the following settings at the top level of your YAML file and restart the service after the configuration check passes:
external-ui: /var/lib/mihomo/ui
On the local machine, open http://127.0.0.1:9090/ui/ in a browser. If the dashboard asks for a backend address and secret, use the control API settings above. Downloading only the dashboard source without a built release, or extracting it into an extra nested directory, can prevent the page from loading.
For a remote server, use SSH local port forwarding. In the command below, proxy-host is an alias already defined in your local SSH configuration; set it up to point to the actual host first:
ssh -N -L 19090:127.0.0.1:9090 proxy-host
Once the tunnel is up, open http://127.0.0.1:19090/ui/ locally and set the dashboard backend to the local port 19090. Keep the server bound to loopback; there's no need to expose the control port to the public internet. The tunnel doesn't replace API key authentication; both protections still apply.
Common issues: check architecture, ports, permissions, and configuration
| Symptom | Check | What to do |
|---|---|---|
| Exec format error | uname -m and file /usr/local/bin/mihomo | Choose a binary for the matching architecture and make sure it was extracted correctly. |
| Illegal instruction | CPU instruction set and release build requirements | Choose a build compatible with older CPUs instead of repeatedly restarting the same binary. |
| address already in use | sudo ss -lntp | Check whether a desktop client or an older process is using ports 7890 or 9090. |
| permission denied | Check file read access, directory execute permissions, and service sandbox restrictions | Check permissions as the service account; don't change every directory to 777. |
| Dashboard returns 401 | Does the dashboard secret match the YAML configuration? | Update the connection credentials and check whether you're connected to a different core instance. |
| Browser works, but command-line tools fail | Is the terminal explicitly configured to use the proxy? | Test separately with curl's --proxy option, then configure the application's environment. |
When a desktop client and standalone service run at the same time, check which core you're connected to. The two instances can coexist on different ports, but changing the selected node in the client won't change the other service's proxy policy. For everyday use, designate one primary instance to avoid changing settings in the wrong dashboard.
Check a deployment in four stages: the process stays running, the expected ports are listening, the application actually uses one of those ports, and the connection matches the expected rule and node. Following these steps in order makes issues easier to isolate than switching to TUN, disabling the firewall, or reinstalling repeatedly. For configuration structure and rule order, see the Clash configuration guide.