Run mihomo on Linux: Desktop Clients and systemd Setup

Compare Linux desktop clients with a standalone systemd setup for mihomo, including binary locations, config directories, service files, and shell proxy variables.

Choose a setup: desktop client or standalone service

If you use a desktop environment and regularly switch proxy groups, a Clash client with a graphical interface is usually the better fit. For servers managed over SSH, or when mihomo needs to keep running after logout, use mihomo with systemd. mihomo is the proxy core in the Clash Meta ecosystem; it does not include a full desktop interface. Downloading the core binary does not install a graphical client.

What to compareDesktop clientStandalone systemd service
Day-to-day useImport a subscription and choose a proxy group in the UIEdit YAML and inspect service logs
Configuration sourceConfig and overrides managed by the clientAn explicitly specified config.yaml
When it startsUsually at desktop login, depending on client settingsAt system boot once the service is enabled
Best suited toPersonal Linux desktopsServers and always-on hosts

Desktop setup: install, import, and verify

Check your architecture and package format

Start with uname -m. The common values x86_64 and aarch64 correspond to the amd64 and arm64 architectures, respectively; do not treat them as interchangeable just because both are 64-bit. On Debian and Ubuntu, choose a DEB package if the client provides one; on Fedora and similar distributions, choose RPM. Check the client’s documentation for any additional runtime requirements for AppImage.

On the client downloads page, select Linux, then check the architecture and package format. For a DEB package already saved in the current directory, open it in your file manager or install it with sudo apt install ./ followed by the actual filename. Do not pass a standalone core archive to the package manager as if it were a DEB package.

Check the key features in the interface

  1. Config: Import a subscription URL or local YAML file, then set it as the active configuration. Subscription URLs may contain credentials; do not post them in public logs.
  2. Proxies: Set the mode to Rule, then check that the proxy groups referenced by your rules have a working proxy selected. A successful import does not mean the right proxy group is selected.
  3. Settings: Check the actual mixed port and note whether it is 7890, then enable the system proxy if needed. The client may label this section “Settings” or “Network Settings”; use the name shown in your version.
  4. Logs: Make a request and look for config parsing errors, port binding failures, or upstream connection errors. Do not rely on the tray icon alone to determine where traffic is going.

The desktop system proxy only affects applications that honor that setting. Terminal tools such as curl, some developer tools, and background services may not use it, so this guide also covers shell proxy variables below. If you chose the desktop setup, you can skip the standalone service installation steps and read the verification and troubleshooting sections instead.

Command-line setup: install the binary and set up its config directory

The commands below are for Debian- and Ubuntu-based systems that use systemd and assume your account has sudo access. Containers, minimal distributions, and WSL environments may not use systemd as their init system. Check with ps -p 1 -o comm= first; if the output is not systemd, do not use the service management commands as written.

Install the executable

Get a Linux core package from the project’s release channel that matches your architecture and CPU requirements. Extract it, rename the executable to mihomo, and place it in the current directory. The commands below do not download any files; before installing, make sure the file in the current directory is the core you intend to use.

uname -m
ps -p 1 -o comm=
sudo install -m 0755 ./mihomo /usr/local/bin/mihomo
/usr/local/bin/mihomo -v

-v displays the version actually installed. Save this output with your deployment notes so you can check config field compatibility when troubleshooting later. Do not infer the core version from the version number shown in a client window.

Create a dedicated account and directories

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
  • /usr/local/bin/mihomo: The executable, managed by root and not writable by the service account.
  • /etc/mihomo/config.yaml: The main config, edited by an administrator and readable by the service account.
  • /var/lib/mihomo: The runtime data directory for caches and other resources saved as required by the config.

Create the account only once. If you are told that it already exists, first check what it is used for; do not delete and recreate it. If your providers, rule sets, or databases use relative paths, check where they resolve against the data directory specified with -d. Do not assume paths are always relative to the main config file.

Start with a minimal config to confirm the service runs

For the initial setup, verify that the program starts, reads its config, and listens on a port before adding a real subscription. The direct-connection config below is only for startup checks and contains no remote proxies. Even if a request succeeds, it only confirms that the local proxy endpoint works—not that traffic is going through a proxy node.

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 accepts both HTTP and SOCKS connections on the same port. allow-lan: false restricts the service to local use. mode: rule processes connections in rule order; the final MATCH,DIRECT sends all requests directly in this example. The -t flag checks the config; it does not keep the service running in the background.

Check three things when switching to a real proxy config

  1. Subscription format: The core’s -f flag reads a config file; it does not accept a subscription URL directly. A list of proxy nodes alone is not a complete config with general settings, proxy groups, and rules.
  2. Referenced names: Every proxy group referenced in rules must exist, and each proxy or provider referenced by a group must resolve correctly.
  3. Resource permissions: Providers and rule sets that need to be downloaded or updated should be stored in a data directory writable by the service account, not in the read-only /etc/mihomo directory.

Create the systemd service and enable it at boot

Save the following as /etc/systemd/system/mihomo.service. This service runs as a dedicated unprivileged account and is intended for the local explicit-proxy setup described here. StateDirectory manages the runtime data directory, while ProtectSystem=strict limits where the service can write.

[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
sudo systemctl daemon-reload
sudo systemctl enable --now mihomo.service
systemctl is-enabled mihomo.service
systemctl is-active mihomo.service
sudo systemctl status mihomo.service --no-pager -l
sudo journalctl -u mihomo.service -b -n 80 --no-pager

enable configures the service to start at boot; --now also starts it immediately. Whether network-online.target actually waits depends on your system’s network management services and does not guarantee that every remote address is reachable. If a network resource fails to download, check the logs to distinguish a DNS, connection, or permissions problem.

After editing the service file, run daemon-reload before restarting it. If you only changed the YAML, run the earlier -t check first; if it passes, run sudo systemctl restart mihomo.service. This service does not define ExecReload, so do not assume systemctl reload is configured for hot reloads.

Terminal proxy: test explicitly, then set environment variables

Confirm that the local endpoint is listening

sudo ss -lntp 'sport = :7890'
curl --noproxy "" --proxy http://127.0.0.1:7890 \
  --connect-timeout 10 --max-time 30 \
  -I https://clash03.com/

The first command checks for a TCP listener. The expected local address is 127.0.0.1:7890, not 0.0.0.0:7890, which would expose it on every network interface. The second command specifies the proxy explicitly to avoid interference from previously set environment variables; it does not guarantee latency or exit location. With the minimal direct-connection config, traffic still goes straight to the destination site.

Set a proxy for the current shell

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

curl --connect-timeout 10 --max-time 30 -I https://clash03.com/

The value of https_proxy is still http://: it means HTTPS connections are made through a local HTTP proxy, not that the local proxy endpoint must use TLS. For programs that support it, socks5h:// passes the destination hostname to the SOCKS proxy for resolution. If a program prioritizes http_proxy or https_proxy, it may not use all_proxy.

These variables affect only the current shell and child processes started afterward. They do not apply automatically to browsers that are already running, other login sessions, or system services. To make them persistent, add them to ~/.bashrc or ~/.zshrc, depending on your shell. Do not add proxy variables to every system service just to configure one program.

unset http_proxy https_proxy all_proxy no_proxy
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY

After unsetting the variables, newly started commands will no longer get their proxy settings from them. sudo may filter environment variables by default, and tools such as Git and package managers may have their own proxy settings. If curl works but other commands do not, check each tool’s configuration instead of immediately switching proxy nodes.

Common issues: troubleshoot by process, config, and connection

SymptomCheck firstWhat to do
Exec format erroruname -m versus binary architectureUse a build for the matching architecture
Illegal instructionCPU instruction set required by the buildChoose a compatible build provided by the project
address already in useProcess occupying a port shown by ss -lntpStop the duplicate core or change the port consistently
permission deniedConfig read permissions and write access to the data directoryCorrect ownership, paths, or security settings
Config validation passes, but requests failProxy group selection, upstream reachability, and resource loading logsDistinguish valid syntax from a working connection

Service keeps restarting

First run sudo systemctl stop mihomo.service to stop the logs from filling up. As the same service account, run the -t check, then inspect the logs for the latest startup attempt. If failures have triggered systemd’s start limit, fix the cause, run sudo systemctl reset-failed mihomo.service, and start the service again. Do not mask YAML errors by simply increasing the retry count.

Service is running, but the terminal cannot connect

First check that the address curl is using matches mixed-port. If you change the port from 7890 to 7891, update the system proxy, shell variables, and each application’s own proxy settings as well. When connecting from another machine, 127.0.0.1 refers to that machine itself, not the host running mihomo.

This service is not configured for TUN

TUN routes traffic through a virtual network device; it is not the same as setting HTTP proxy variables. It requires access to /dev/net/tun, network administration capabilities, and route and DNS configuration. This service deliberately does not grant those permissions. Setting tun.enable to true alone does not guarantee that TUN will work. If you need TUN, plan the required permissions and routes using the relevant client or core documentation, and check for conflicts with other VPNs.

Update and rollback: keep a working config

  1. Before updating, record the output of mihomo -v and back up the main config and current executable. Keep backups containing subscription details access-restricted.
  2. Place the new executable in a temporary location and use it to run -t against the existing config. A successful check only means the config can be parsed; you still need to verify that the service starts correctly.
  3. Stop the service before replacing the executable. Preserve root ownership and executable permissions, then start the service again.
  4. Check systemctl is-active, the latest logs, and an explicit proxy request. If anything looks wrong, restore the previous executable and config, then restart the service.

A successful deployment should meet all three criteria: the service is healthy, the local port is as expected, and a real request uses the intended proxy rule. Desktop clients are best for interactive management; systemd is suited to fixed paths and long-running services. Whichever option you choose, keep the config location, proxy endpoint, and update process clear to avoid running multiple cores with no way to tell which one is handling traffic. For field details, see the Complete Configuration Guide. For your first config import, see Quick Start.

Download ClashChoose a client for your platform