Install Clash on Linux: Run mihomo with systemd
Set up mihomo on Linux desktops or headless servers, including installation, config paths, systemd services, and terminal proxy variables.
Choose a deployment method: desktop client or standalone core
On a desktop, use a graphical client to manage subscriptions, proxy groups, and connection logs. On an SSH-only server, run mihomo from the command line as a systemd service. mihomo, commonly known as the Clash Meta core, reads the configuration, listens on proxy ports, and applies routing rules. A graphical client provides an interface around the core; they serve different roles.
| Environment | Recommended setup | Main management interface |
|---|---|---|
| Desktop environments such as Ubuntu and Fedora | A Linux-compatible graphical client | Client screens for configuration, proxy settings, and connections |
| Servers running Debian, Ubuntu, and similar distributions | mihomo with a systemd service | Config files, systemctl, and journalctl |
| OpenRC or minimal container environments | Use the existing process manager | Don't use this systemd unit as-is |
On a desktop, choose a Linux package from the client downloads page. Debian and Ubuntu generally use DEB packages; Fedora generally uses RPM. Check your CPU architecture as well. Import your configuration, enable the system proxy provided by the client, and test it in a browser. Desktop system proxy settings may not cover terminal tools, games, or every background service, so configure terminal proxies separately as described below.
Check your architecture and install the mihomo binary
First check the system architecture and init process. These commands work on common GNU/Linux distributions. If PID 1 isn't systemd, the service management commands below won't apply as written.
uname -m
ps -p 1 -o comm=
getconf GNU_LIBC_VERSION
x86_64corresponds to amd64. For older CPUs, choose a compatible build identified on the project's releases page. If you're unsure which instruction sets your CPU supports, don't pick a more aggressively optimized build.aarch64usually corresponds to arm64. For 32-bit ARM, check both the CPU and OS bitness; the device's appearance isn't enough to identify the right build.- Alpine typically uses musl and doesn't use systemd by default. Choose a compatible build and service manager instead; don't assume glibc-based installation instructions will work.
Download an archive for your system from an official mihomo project release channel. Extract it, place the executable in the current directory, and name it mihomo. The commands below use that extracted file and don't rely on version-specific archive filenames.
file ./mihomo
chmod 0755 ./mihomo
./mihomo -v
sudo install -m 0755 ./mihomo /usr/local/bin/mihomo
/usr/local/bin/mihomo -v
Use file to check the executable's architecture and -v to confirm the core starts on this machine. Keep the output for troubleshooting. For Exec format error, check the architecture and make sure you're not trying to run the archive itself. For Illegal instruction, check which CPU instruction sets the build requires.
Keep configuration and runtime data separate
Store the configuration at /etc/mihomo/config.yaml and writable runtime data at /var/lib/mihomo. Run the service as a dedicated user; listening on an unprivileged port such as 7890 doesn't require root. The account creation commands below are for systems such as Debian, Ubuntu, and Fedora that provide useradd.
getent group mihomo >/dev/null || sudo groupadd --system mihomo
id mihomo >/dev/null 2>&1 || sudo useradd \
--system --gid mihomo --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 a mihomo account already exists, confirm it's in the right group. The nologin path varies across distributions; check it with command -v nologin and adjust as needed. The dedicated account is for running the service, not interactive logins.
Test the startup process with a minimal configuration
This complete local proxy configuration sends all requests directly to their destinations. It tests file permissions, port listening, and service startup; it includes no proxy nodes and does not change your public IP address. For a first deployment, use it to rule out installation issues before switching to your actual subscription configuration.
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
Use -f to specify the config file and -d to specify the core's data directory; they can be different. If rule sets, proxy groups, or geodata use local files, check their paths and make sure the dedicated user can access them. Don't put files in a regular user's private home directory and expect a system service to read them.
Replace it with your subscription configuration
Get a complete Clash YAML configuration before replacing the file. A Base64-encoded node list or a single URI isn't a complete configuration and can't be saved directly as config.yaml. A complete configuration also needs the corresponding proxy groups and rules, and every policy name referenced in the rules must exist.
- Keep a working copy of your configuration so you can recover if an edit fails.
- Save the actual configuration to
/etc/mihomo/config.yaml, retaining theroot:mihomoownership and0640permissions. - Keep the mixed port set to 7890 and LAN access disabled. If you change the port, update all the test commands below as well.
- Run the
-tcommand above again. If your configuration uses GEOIP, GEOSITE, or remote rule sets, provide the required resources and make sure the system can reach the network to fetch them the first time.
Create a systemd service and enable it at boot
Create /etc/systemd/system/mihomo.service. This service runs as a regular user and provides explicit HTTP/SOCKS proxy access; it doesn't create a TUN interface. The absolute paths in the unit match the installation paths used above.
sudo tee /etc/systemd/system/mihomo.service >/dev/null <<'UNIT'
[Unit]
Description=mihomo proxy core
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=mihomo
Group=mihomo
StateDirectory=mihomo
StateDirectoryMode=0750
WorkingDirectory=/var/lib/mihomo
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
UNIT
sudo systemctl daemon-reload
sudo systemctl enable --now mihomo
systemctl is-active mihomo
sudo systemctl status mihomo --no-pager
StateDirectory provides a writable directory for runtime data, while ProtectSystem=strict restricts writes elsewhere in the filesystem. Root owns the config, which the core reads but can't modify. Put remote rule sets and other files that need to be written in the data directory. network-online.target marks the end of the system's network startup process; it doesn't guarantee that a particular subscription server is reachable.
Know when to reload configuration versus the service unit
- For YAML changes only: test the configuration, then run
sudo systemctl restart mihomo. - After changing the service unit, run
sudo systemctl daemon-reload, then restart the service. - To stop the service temporarily, run
sudo systemctl stop mihomo. - To disable startup at boot and stop the service now, run
sudo systemctl disable --now mihomo.
This guide doesn't define ExecReload, so don't rely on systemctl reload mihomo as a generic reload command. Restarting interrupts active connections; avoid doing it during long downloads or remote transfers.
Set a terminal proxy and test a real request
An active service only tells you the process is running. First confirm that port 7890 is listening, then send a specific request through it. The mixed port accepts both HTTP proxy and SOCKS5 connections; test each one below.
sudo ss -lntp 'sport = :7890'
curl --noproxy "" --proxy http://127.0.0.1:7890 \
--connect-timeout 10 --max-time 20 -I https://example.com/
curl --noproxy "" --proxy socks5h://127.0.0.1:7890 \
--connect-timeout 10 --max-time 20 -I https://example.com/
socks5h sends the destination hostname to the proxy for resolution; with socks5, the client usually resolves the hostname first. HTTP proxies typically use CONNECT to tunnel HTTPS traffic, so http://127.0.0.1:7890 is the correct proxy URL and doesn't mean the destination website uses plain HTTP.
Set variables for the current terminal session
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 20 -I https://example.com/
These variables affect only the current shell and programs started from it that support them. Once you've confirmed they're needed long term, add them to ~/.bashrc for Bash or ~/.zshrc for Zsh, then open a new terminal. Support for uppercase and lowercase variable names varies by tool; for curl's HTTP proxy, use lowercase http_proxy.
sudo often clears environment variables, and systemd services don't automatically read interactive shell configuration. So a successful terminal curl test doesn't mean package managers or background jobs are using the proxy. Before configuring those tools separately, confirm they actually need it; avoid injecting proxy variables into every system service.
Unset the proxy variables
unset http_proxy https_proxy all_proxy no_proxy
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
If you added the variables to a shell configuration file, remove those lines too. With the minimal direct-connection configuration above, a successful test only confirms the local proxy listener works. After switching to your actual configuration, check the logs to confirm which rule and outbound policy matched; an HTTP response alone doesn't prove a proxy node was used.
Troubleshoot using logs, ports, and permissions
Save the error context and change one variable at a time. Don't change DNS, rules, and permissions all at once; even if the connection comes back, you won't know what fixed it. The commands below show recent logs and follow new logs, respectively. Press Ctrl+C to stop the live log.
sudo journalctl -u mihomo -b -n 100 --no-pager
sudo journalctl -u mihomo -f
| Symptom or log | Check first | What to do |
|---|---|---|
| status=203/EXEC | Binary path, execute permissions, and architecture | Check ExecStart and run mihomo -v again |
| address already in use | Is another client using port 7890? | Use ss to identify the process; stop the duplicate core or change the port consistently |
| permission denied | Permissions on the config and runtime directories | Test the configuration as the mihomo user and check permissions on every parent directory |
| YAML parsing error or missing policy | Indentation, config syntax, and policy names | Use spaces for indentation; check references in rules and proxy-groups |
| Service runs, but requests time out | Destination resolution, outbound policy, and remote resources | Test the minimal direct-connection config first, then test the actual nodes and rules |
| Start request repeated too quickly | Cause of repeated startup failures | After fixing the issue, run reset-failed, then start the service |
To check path permissions, run namei -l /etc/mihomo/config.yaml. After fixing repeated startup failures, run sudo systemctl reset-failed mihomo and sudo systemctl start mihomo. Don't work around permission issues by making files writable by everyone.
Understand TUN requirements and keep a rollback path
Consider TUN only if you need to route traffic from apps that don't support explicit proxies. TUN involves a virtual interface, routes, DNS, and permissions; setting allow-lan to true won't enable it. This guide's service runs as a regular user with NoNewPrivileges=true, so it can't be used as a TUN service without changes.
On Linux, TUN typically requires access to /dev/net/tun and permissions such as CAP_NET_ADMIN. Automatic routing, DNS hijacking, and different network stacks may require more. Before changing routes on a remote host, keep a second SSH session open and make sure you have console access for recovery. In a container, check that the host allows device access and the required network capabilities; changing YAML inside the container isn't enough.
Upgrade the core with a rollback plan
- Record the current
mihomo -voutput and keep a backup of the configuration. - Run
-von the new core in a temporary location, then test the existing configuration as the service user. - Stop the service, back up the old binary, replace it, and start the service again.
- Check the logs and port 7890, then repeat the HTTP and SOCKS5 request tests.
sudo systemctl stop mihomo
sudo cp -p /usr/local/bin/mihomo /usr/local/bin/mihomo.previous
sudo install -m 0755 ./mihomo /usr/local/bin/mihomo
sudo systemctl start mihomo
sudo journalctl -u mihomo -b -n 50 --no-pager
Here, ./mihomo is the new binary you've already tested. To roll back, stop the service, reinstall /usr/local/bin/mihomo.previous as /usr/local/bin/mihomo, then start the service. Core versions and config syntax can change together, so keep backups of both the binary and the configuration.
Validate the deployment with three checks: the service starts after a reboot, port 7890 listens only on the expected address, and requests from a specified app match the expected policy. This distinguishes “the binary is installed,” “the proxy listener works,” and “traffic is actually routed by the rules,” and makes later troubleshooting easier.