Run the mihomo core on Linux: CLI setup, config paths, and systemd autostart
Install mihomo on Linux: choose the right binary, set up config.yaml and a systemd service, then configure your desktop proxy.
1. Scope: Start with a local proxy, then consider TUN
This guide uses a systemd-based Linux distribution such as Debian or Ubuntu. You’ll need sudo access and the gzip, curl, and iproute2 packages, plus CA certificates. First, check that PID 1 is systemd. The service-management steps may not apply as written in containers, some WSL environments, or systems using a different init system.
ps -p 1 -o comm=
uname -m
sudo apt update
sudo apt install gzip curl iproute2 ca-certificates
mihomo is a proxy core evolved from Clash Meta, not a desktop app with a graphical interface. The core reads YAML, establishes proxy connections, and matches rules. It does not provide desktop menus, subscription management, or a system proxy toggle.
- Local app endpoint: This guide uses
127.0.0.1:7890, a single mixed port for both HTTP proxy and SOCKS5 requests. - Service permissions: Use a dedicated, non-login user. A standard proxy port does not require root privileges.
- Traffic scope: Verify an explicit proxy first. This guide does not configure transparent proxying, firewall forwarding, or a TUN virtual interface.
2. Choose the right binary: Check the architecture, extract, and install
Check the output of uname -m, then choose the matching Linux core file from the download page. Don’t use a client DEB or RPM package as a standalone core archive, and don’t guess the processor architecture based on the device brand.
| Command output | Matching build | Before you install |
|---|---|---|
x86_64 | Linux amd64 | If builds are available for different CPU instruction sets, choose a compatible version as noted in the release information. |
aarch64 | Linux arm64 | Confirm that you’re running a 64-bit userspace. |
armv7l | The matching Linux ARM build | Check the ARM version and floating-point ABI. An arm64 binary is not a drop-in replacement. |
For the steps below, the downloaded .gz core archive is saved in the current user’s Downloads directory and renamed locally to mihomo.gz. This is just a local filename; it may differ from the name of the release file. If you downloaded an uncompressed binary, skip the gzip extraction step.
gzip -dc "$HOME/Downloads/mihomo.gz" > "$HOME/Downloads/mihomo"
chmod 0755 "$HOME/Downloads/mihomo"
"$HOME/Downloads/mihomo" -v
sudo install -m 0755 "$HOME/Downloads/mihomo" /usr/local/bin/mihomo
/usr/local/bin/mihomo -v
The version command should print the version of that file. If you see Exec format error, first check the CPU architecture and whether the file is compressed. If you see Illegal instruction, check whether you chose a build that requires a newer instruction set. Adding sudo repeatedly won’t fix either error.
3. Directory layout: Config in /etc, runtime data in /var/lib
Fixed paths prevent manual and systemd launches from loading different files. This guide explicitly uses -f to specify the config file and -d to set the data directory, rather than relying on the logged-in user’s default config path.
| Path | Purpose | Permissions |
|---|---|---|
/usr/local/bin/mihomo | Core executable | Installed by root; the service user only needs permission to execute it. |
/etc/mihomo/config.yaml | Main config file | Edited by root; readable by the mihomo group. |
/var/lib/mihomo | Cache, rule data, and provider files | Writable by the service user. |
/etc/systemd/system/mihomo.service | systemd service unit | Managed by root. |
Create the service account and directories during initial setup. If an account with the same name already exists, check the output of id mihomo before deciding whether to reuse it. Don’t rerun the account creation command and ignore the error.
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
Subscription files may contain server addresses, passwords, and access tokens. Don’t store them in a public web directory or paste them into a support ticket. Relative paths for rule and proxy providers are generally resolved against the data directory. Use paths such as ./rules/ and ./providers/ within that directory, and check that the service user can write to them.
4. config.yaml: Verify the listener before importing your real config
Use a minimal config to verify startup
The YAML below contains no proxy nodes, so all requests go directly to their destination. It verifies that the core starts, can read its files, and listens on the local port; it does not prove that a remote proxy works. Establish this baseline first to separate deployment issues from subscription issues.
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
proxies: []
proxy-groups: []
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
Use -t to validate the config. Once it passes, run mihomo in the foreground. In another terminal, run the curl test below. Press Ctrl+C when done so the foreground process doesn’t compete with the systemd service for the port.
sudo -u mihomo /usr/local/bin/mihomo \
-d /var/lib/mihomo -f /etc/mihomo/config.yaml
A complete subscription needs nodes and policy definitions
Get a complete YAML config from your provider using its mihomo or Clash Meta configuration link, then save it locally as Downloads/config.yaml. A list of node URIs, Base64 text, or webpage content can’t be used directly as the main config. A file containing only proxies is also not a complete config with rules and policy groups.
sudo cp -a /etc/mihomo/config.yaml /etc/mihomo/config.yaml.bak
sudo install -o root -g mihomo -m 0640 \
"$HOME/Downloads/config.yaml" /etc/mihomo/config.yaml
sudoedit /etc/mihomo/config.yaml
sudo -u mihomo /usr/local/bin/mihomo \
-t -d /var/lib/mihomo -f /etc/mihomo/config.yaml
- In your editor, check the top-level values for
mixed-port: 7890,allow-lan: false, andbind-address: 127.0.0.1. Edit existing keys instead of adding duplicates. - For the initial setup, disable
tun.enablein the config. If DNS is configured to listen on port 53, disable it for now or plan the required permissions separately; this service unit does not grant them. - Check that every policy name referenced under
rulesexists inproxy-groupsor the node list. Indent YAML with spaces, not tabs. - For the first run, consider removing any nonessential controller settings. If you need an external controller, bind it to the loopback address and set an access key. Never expose it directly to the public internet.
5. systemd service: Set a fixed user, paths, and startup options
Make sure the foreground process has exited before creating the service file. Absolute paths avoid issues where systemd can’t find the program because its environment differs from an interactive shell. network-online.target only indicates that the system’s network startup process has completed; it doesn’t guarantee that subscription URLs or remote nodes are reachable.
sudo tee /etc/systemd/system/mihomo.service > /dev/null <<'UNIT'
[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=5s
UMask=0077
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
UNIT
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
sudo journalctl -u mihomo.service -b -n 80 --no-pager
enable --now enables the service at boot and starts it immediately. is-enabled should show enabled, and is-active should show active. These results confirm the process state only. Test access to your destination in the next section.
Handle config changes and service file changes separately
- YAML changes only: Run the full
mihomo -tconfig check first. If it passes, runsudo systemctl restart mihomo. - Service file changes: Run
sudo systemctl daemon-reload, then restart the service. - Stop temporarily: Run
sudo systemctl stop mihomo. This won’t disable startup at the next boot. - Disable and stop: Run
sudo systemctl disable --now mihomo.
This service does not define ExecReload, so don’t assume systemctl reload mihomo will work. Checking the config and restarting is more explicit. A restart interrupts existing connections, so avoid doing it during downloads or remote sessions.
6. Test the connection: Check the listener, HTTP, and SOCKS5 separately
Check the port before testing a request. Use -q to prevent the user’s curl config file from affecting the result, and --noproxy "" to prevent an existing proxy bypass list from skipping this test. The address below is for demonstrating the request flow, not for benchmarking proxy nodes.
sudo ss -lntp | grep ':7890'
curl -q --noproxy "" --proxy http://127.0.0.1:7890 \
--connect-timeout 10 --max-time 30 -I https://example.com/
curl -q --noproxy "" --proxy socks5h://127.0.0.1:7890 \
--connect-timeout 10 --max-time 30 -I https://example.com/
socks5h sends the destination hostname to the proxy for resolution. Unlike socks5, curl does not resolve the hostname locally. With mihomo, the actual DNS resolution path also depends on the core’s DNS settings, so this option alone does not tell you where all DNS traffic goes.
- Connection refused: Check that the core is running, the port matches, and the listener isn’t bound to a different address.
- Proxy connects, but the destination times out: Check the matched rule, selected policy, and node instead of changing the local listening port again.
- A request works with the minimal config: This only confirms that the explicit proxy endpoint works; traffic is still going direct.
- Verify a real node: With the full config loaded, watch
journalctl -u mihomo -fand check the rule and outbound policy matched by the request. A successful HTTP CONNECT handshake does not mean the destination site has responded successfully.
7. Desktop and terminal: Configure each proxy separately
GNOME desktop settings
In GNOME, go to Settings → Network → Network Proxy and select Manual. Set the host to 127.0.0.1 and the port to 7890 for both HTTP and HTTPS. You can use the same host and port for SOCKS. The HTTPS field sets a proxy for HTTPS destinations; it doesn’t mean the local port needs to provide TLS.
You can also configure GNOME from a terminal opened in the current desktop user’s session. Don’t use sudo: it may change root’s settings or prevent access to the current user’s session bus.
gsettings set org.gnome.system.proxy.http host '127.0.0.1'
gsettings set org.gnome.system.proxy.http port 7890
gsettings set org.gnome.system.proxy.https host '127.0.0.1'
gsettings set org.gnome.system.proxy.https port 7890
gsettings set org.gnome.system.proxy.socks host '127.0.0.1'
gsettings set org.gnome.system.proxy.socks port 7890
gsettings set org.gnome.system.proxy ignore-hosts "['localhost', '127.0.0.0/8', '::1']"
gsettings set org.gnome.system.proxy mode 'manual'
Environment variables for the current terminal
Desktop proxy settings don’t guarantee that terminal apps will use the same proxy. For command-line tools that support proxy environment variables, set the following in the current shell. They affect only programs started afterward. Support for variable capitalization, SOCKS, and bypass lists varies by tool.
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
If your browser is set to use a different proxy manually, switch it to the system proxy or enter the port above. In containers and virtual machines, 127.0.0.1 refers to that environment itself, not the host. This loopback-only setup does not provide a proxy endpoint directly to them.
8. Common errors and rollback steps
| Symptom | Check first | What to do |
|---|---|---|
status=203/EXEC | Executable path, execute permissions, and file architecture | Run /usr/local/bin/mihomo -v manually and verify the absolute path in the service file. |
permission denied | Read access to the main config, directory access, and write access to the data directory | Run the config check as sudo -u mihomo; don’t run as root to mask a permissions issue. |
address already in use | Port 7890 or another configured listening port | Use sudo ss -lntup to find the process using the port, then stop the duplicate core or choose another port. |
| Failed to download resources | Check whether the config references Geo data, rule providers, or proxy providers | Check the resource URLs, directory permissions, and whether direct access is available during startup. Don’t keep reinstalling the binary. |
| Terminal still has no network access after quitting the desktop app | Proxy variables are still set in the shell | Unset the variables and check whether the command has its own proxy settings. |
If the service won’t start after a config update, first check the logs for the first meaningful error. If the recent YAML change caused it, restore the backup you made, validate it, and restart. The original backup from this guide is a direct-connection baseline; restoring it won’t bring back your real proxy nodes.
sudo cp -a /etc/mihomo/config.yaml.bak /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
Before disabling the service, switch apps back to a direct connection. Undo the settings in GNOME and the current terminal separately using the steps below. If you added the variables to a shell startup file, remove those lines and reopen the terminal.
gsettings set org.gnome.system.proxy mode 'none'
unset http_proxy https_proxy all_proxy no_proxy
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
sudo systemctl disable --now mihomo
Keep the final verification order consistent: the binary runs; the service account can read the config; config validation passes; port 7890 is listening; a request through the specified proxy succeeds; logs show the expected policy; and the service recovers after a reboot. Verify each layer separately to identify whether an issue lies in deployment, config, the app endpoint, or the remote connection.