Choose a deployment method and traffic entry point
Clash-compatible clients on Linux usually consist of a graphical interface and a proxy core. mihomo is the proxy core: it reads YAML configuration, listens on local ports, and applies routing rules. The GUI client handles subscription imports, configuration management, proxy group selection, and system proxy or TUN settings. They are separate packages and do not share a standard configuration directory.
Use a GUI client if you have a desktop environment such as GNOME or KDE and need to switch nodes frequently. On an SSH-only server, or when you need startup on boot and centralized logs, running mihomo with systemd makes permissions and file ownership easier to manage. The command-line setup in this guide assumes a standard Linux host with systemd; it does not apply to environments managed only by a container process manager.
| Item | Desktop GUI client | mihomo service |
|---|---|---|
| Configuration source | Client configuration or subscription page | /etc/mihomo/config.yaml, explicitly set in this guide |
| How it starts | Desktop launcher or automatic login startup | systemctl enable --now mihomo |
| Log location | Client log viewer | journalctl -u mihomo |
| Basic traffic entry point | System proxy or per-app proxy | Applications connect directly to 127.0.0.1:7890 |
| Permission boundaries | Keep the regular desktop session separate from privileged services | Run the basic proxy under a dedicated non-root user |
Desktop setup: Install a GUI client and import a configuration
1. Check your distribution, architecture, and package format
Choose a Linux-compatible client, such as Clash Verge Rev or FlClash, from the client downloads page. Check your system details first rather than choosing a package based only on your computer model. x86_64 usually corresponds to x64 or amd64 on the release page; aarch64 usually corresponds to arm64. In addition to matching the processor architecture, check the client’s required distribution version and runtime libraries.
cat /etc/os-release
uname -m
Debian and Ubuntu generally use DEB packages, while Fedora and similar distributions generally use RPM packages. For simplicity, rename the downloaded file locally to client.deb or client.rpm; these are not the project’s actual release filenames. Go to the directory containing the file and run only the command for your distribution:
# Debian / Ubuntu
sudo apt install ./client.deb
# Fedora
sudo dnf install ./client.rpm
Launch the client from your desktop’s application menu; do not run the entire GUI with sudo. If service mode or TUN permissions are needed, use the client’s service installation or authorization option. This avoids creating root-owned user configuration that prevents you from saving subscriptions the next time you launch as a regular user.
2. Import a subscription and verify the active configuration
- Import a valid Clash-compatible subscription URL or a local YAML file from the client’s configuration or subscription page. Menu names may vary between client versions.
- Select the configuration you just imported and set it as the active configuration. A successful download does not mean the configuration is enabled.
- Choose an available node in a proxy group, then check the logs for configuration parsing errors. Proxy group names must match the names referenced by the rules.
- Check the active configuration or runtime information for the HTTP, SOCKS, or mixed listening port. The examples below use
7890; you do not need to force an existing client to use this port. - Test with an explicit proxy first, then enable the system proxy. Test TUN only if you need to route traffic from applications that ignore proxy settings.
A GUI client may combine subscriptions, overrides, and interface options to generate a runtime configuration. Do not edit this temporary generated file directly: your changes may disappear when the subscription is updated or you switch configurations. Use the client’s override mechanism for persistent changes to ports, rules, or DNS. Do not assume it reads ~/.config/mihomo/config.yaml.
3. Understand the difference between desktop and shell proxy settings
A client’s “system proxy” setting usually changes the desktop environment’s proxy configuration. Browsers may honor it, but curl, package managers, and background services launched from a new terminal may not. Conversely, running export in a terminal does not change an already running browser. When testing, note the specific application, proxy entry point, and port—not just that “the proxy is on.”
Headless setup: Install the core and create dedicated directories
This setup uses fixed paths to avoid confusing the current SSH user’s home directory with the service’s working directory. Before installing the core, use the output of uname -m to select a Linux build from mihomo’s official releases that matches your architecture and CPU instruction set. Extract the downloaded archive and rename the executable to mihomo locally. Run the following commands from the directory containing that file.
sudo install -m 0755 ./mihomo /usr/local/bin/mihomo
/usr/local/bin/mihomo -v
Use -v to check the installed version. If you see Exec format error, first check that you are not trying to run the archive itself and that you selected the right architecture. If you see Illegal instruction, check the build’s instruction set requirements. Downloading a build that matches the host is more useful than repeatedly changing file permissions.
Create a dedicated service account
This account runs only the proxy service and is not used for interactive logins. Skip account creation if it already exists. If your distribution’s nologin binary is in a different location, first check with command -v nologin and adjust the path. Root manages the configuration; the service user writes runtime data.
getent passwd 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
/usr/local/bin/mihomo: the core executable, which the regular service user cannot overwrite./etc/mihomo/config.yaml: the main configuration, which may contain node credentials or subscription URLs and should have restricted read access./var/lib/mihomo: the data directory set with-d, used for runtime state, cache, and resources required by the configuration.
This guide explicitly sets the main configuration with -f rather than relying on a default directory. If your actual configuration includes proxy-providers, rule-providers, or external data files, also check their paths, read permissions, and download requirements. Copying the main YAML file alone does not ensure that all dependencies are in place.
Test the listening port with a minimal configuration first
To separate installation issues from subscription issues, start with the direct-connection test configuration below. Create config.yaml in the current directory and add the following content. This tests only whether the core starts, accepts proxy requests, and connects directly to the internet; it does not include any proxy nodes or change your exit location.
mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
tun:
enable: false
rules:
- MATCH,DIRECT
mixed-port accepts both HTTP and SOCKS proxy requests on the same port. allow-lan: false restricts access to the local machine; the example also specifies a loopback address. The controller API is disabled for now, and there is no need to open port 7890 in your cloud server’s security group.
sudo install -o root -g mihomo -m 0640 \
./config.yaml /etc/mihomo/config.yaml
sudo -u mihomo /usr/local/bin/mihomo \
-t -d /var/lib/mihomo -f /etc/mihomo/config.yaml
-t checks whether the current core can load the configuration; it does not test node connectivity. For YAML errors, first check indentation, spaces after colons, and field types. For permission errors, check that the service account can access the configuration file and every parent directory in its path.
Run mihomo persistently with systemd
Create /etc/systemd/system/mihomo.service and add the complete contents below. This service is for the local explicit-proxy setup described above; it does not grant permission to create TUN devices or modify system routes.
[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
StateDirectory=mihomo
StateDirectoryMode=0750
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
[Install]
WantedBy=multi-user.target
StateDirectory lets systemd manage a writable state directory. ProtectSystem=strict restricts writes to system files while leaving the state directory writable. ProtectHome=true prevents the service from accessing users’ home directories, so do not leave provider files in a downloads directory and reference them there. Put dependencies in a directory the service can access, and arrange them according to the paths in your configuration.
sudo systemctl daemon-reload
sudo systemctl enable --now mihomo.service
systemctl status mihomo.service --no-pager
sudo journalctl -u mihomo.service -b -n 80 --no-pager
sudo ss -lntup 'sport = :7890'
enable --now enables startup at boot and starts the service immediately. Whether network-online.target waits for the network depends on the network-wait service enabled by your distribution; it does not guarantee that remote subscription sites are reachable. If startup needs to download remote resources, check the logs to confirm that the requests succeeded.
Update the configuration and upgrade the binary
After editing the YAML, run the earlier -t check as the service user and restart only if it passes. This guide does not define ExecReload, so do not use systemctl reload mihomo as a valid step. Restarting interrupts active proxy connections; choose an appropriate maintenance window.
sudo -u mihomo /usr/local/bin/mihomo \
-t -d /var/lib/mihomo -f /etc/mihomo/config.yaml \
&& sudo systemctl restart mihomo.service
Keep a copy of the existing binary and configuration before upgrading the core. Stop the service, use install to put the new binary in place, check its version, and validate the configuration again before starting the service. Do not overwrite a running executable or assume the new version supports every deprecated field in an older configuration.
Set shell proxy variables and test requests
Make an explicit proxy request first
Run this test on the same host as mihomo. --noproxy "" prevents an existing NO_PROXY setting from bypassing the proxy for this request. An HTTP response shows that the request made progress; assess the specific status code separately.
curl --noproxy "" --proxy http://127.0.0.1:7890 \
--connect-timeout 10 --max-time 30 -I https://example.com/
With the minimal direct-connection configuration, success means only that the local proxy entry point works. After switching to a configuration with real nodes, use the connection logs to check the destination, rule, and proxy group. A page loading successfully alone does not prove that the exit location has changed.
Set proxy variables for the current shell and its child processes
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://example.com/
Using http:// as the value for https_proxy is not a contradiction: it describes the connection to the local HTTP proxy, while HTTPS destinations typically use CONNECT to establish a tunnel. For programs that support it, socks5h:// means the destination hostname is resolved by the SOCKS proxy. Supported variables and precedence vary across tools.
- This set of
exportcommands affects only the current shell and child processes started afterward; it does not change other login sessions. - For persistent settings, add them to your shell’s startup file, such as
~/.bashrc, commonly used by interactive Bash sessions. Not all programs read this file. sudousually filters environment variables. Do not assume thatsudo aptuses the proxy just because a regular curl command succeeds.- systemd system services do not read a user’s
~/.bashrc. Configure proxy settings separately for any other services that need them, and do not set mihomo itself to use its own proxy port.
If the machine previously used uppercase variables, check them and keep the settings consistent. For security reasons, curl does not accept uppercase HTTP_PROXY, so this guide uses lowercase variables. Before stopping the proxy, clear common variables from the current terminal to prevent leftover settings from breaking later commands:
unset http_proxy https_proxy all_proxy no_proxy
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
TUN, remote access, and troubleshooting
TUN involves separate permissions and routing changes
TUN uses a virtual network interface to capture IP traffic that matches routing conditions; it does not guarantee that “all traffic from every application will always use the proxy.” Route exclusions, DNS, IPv6, and existing VPNs all affect the result. On Linux, TUN usually requires access to /dev/net/tun and network administration capabilities. Setting tun.enable to true alone cannot bypass these requirements.
The non-root systemd unit in this guide does not grant these capabilities. If you need TUN, reassess service permissions, DNS interception, and routing based on the current documentation for your client or mihomo; do not just add a privileged command. Before making changes on an SSH server, keep a separate session and access to the cloud console, and plan how to exclude management networks to avoid losing your connection when routes are taken over.
Use SSH forwarding for remote access
To temporarily use a server’s local proxy from your personal computer, set up SSH local port forwarding on your computer instead of exposing the proxy port to the public internet. The example assumes your SSH config already has a host alias named mihomo-host:
ssh -N -o ExitOnForwardFailure=yes \
-L 127.0.0.1:17890:127.0.0.1:7890 mihomo-host
Keep the SSH connection open and set your application’s proxy on your personal computer to 127.0.0.1:17890. The remote 127.0.0.1:7890 refers to the server itself, not your personal computer. If you really need to provide a proxy on your LAN, configure authentication, firewall rules, and access scope separately; enabling allow-lan alone is not enough.
Narrow down the cause based on the symptoms
| Symptom | Check first |
|---|---|
address already in use | Use ss to check for port conflicts. A desktop client and systemd service may both be listening on 7890; keep just one entry point or configure separate ports. |
Permission denied | Check whether the service user can read the main configuration and provider files, and whether the data directory is writable. Do not replace targeted troubleshooting with broad permissions on the entire directory. |
Connection refused | Confirm that the service is running and that its actual listening address and port match those used by the application. A container’s 127.0.0.1 is not the host’s loopback address. |
| Can connect to the port, but requests time out | Check whether the node is reachable, DNS resolution succeeds, and the expected proxy group and rule are selected. Distinguish a working local listener from a working upstream connection. |
| Works in the terminal but not in other applications | Check whether the application supports environment variables, has its own proxy enabled, or was already running before you set the variables. |
| Starts in the terminal but fails as a service | Compare the actual runtime user, working directory, absolute configuration path, and file permissions. Check whether the service references files isolated by ProtectHome. |
Keep three pieces of evidence while troubleshooting: the version output from mihomo -v, the first meaningful error in the service logs, and the proxy address the application actually uses. Remove subscription credentials, node passwords, and controller API keys before sharing logs. For field descriptions, see the full documentation; for importing a subscription and switching configurations, see the setup guide.