· Platform Setup · 13 min read

Deploy mihomo on Ubuntu: Desktop Clients and a Headless systemd Setup

Install a graphical client on Ubuntu Desktop or deploy mihomo on a headless server, with binary placement, config.yaml paths, systemd setup, and TUN permissions.

Choose a Deployment: Desktop Client or Standalone Core

mihomo is the core that reads YAML configuration, matches proxy rules, and establishes connections. It is also known as Clash Meta. A desktop client adds subscription management, proxy group selection, system proxy settings, and logs. Installing the mihomo binary alone does not provide a graphical interface or automatically change Ubuntu's desktop proxy settings.

The commands below target common systemd setups on Ubuntu 22.04 LTS and 24.04 LTS. Containers, WSL, and restricted VPS environments may have different device permissions, so check TUN support separately. This guide does not pin a mihomo version. Run mihomo -v during deployment to record the version in use; keeping this information makes configuration compatibility issues easier to troubleshoot after upgrades.

Use caseRecommended approachWhat you’ll manage
Everyday use on a GNOME desktopGraphical clientSubscriptions, system proxy, and client service mode
Headless Ubuntu servermihomo with systemdConfiguration files, service account, logs, and startup order
Temporary terminal taskLocal proxy portSet a proxy for a specific command; TUN is not required

Choose one method per machine. Running a desktop client's built-in core alongside a standalone systemd service can cause both to compete for port 7890 or create duplicate TUN routes.

Install and Configure a Desktop Client on Ubuntu

Check the Architecture and Package Format

Start by running these commands in a terminal. x86_64 usually corresponds to an amd64 package, and aarch64 usually corresponds to arm64. Don’t infer the architecture from the device brand alone.

uname -m
dpkg --print-architecture
lsb_release -ds

Choose a Linux-compatible client from the client download page, such as Clash Verge Rev, and check its dependencies for your Ubuntu release. On Ubuntu, prefer the .deb package for your architecture. In the download directory, run sudo apt install ./ followed by the actual filename (use Tab completion if needed); apt will resolve available package dependencies. AppImage is another distribution format. Check the client’s release notes to see whether a FUSE compatibility package is needed—don’t reuse the same dependency command across Ubuntu releases.

Subscriptions, System Proxy, and Browser Checks

  1. Open the client’s configuration or subscription page and import a trusted subscription URL or local YAML file. Subscription links often contain access credentials; don’t include them in public screenshots, support tickets, or Git repositories.
  2. Select the imported configuration and choose an available node in the proxy group. Start with Rule mode. It selects an outbound route according to the rules; it does not send all traffic through the proxy.
  3. Check the actual HTTP or mixed-port value in the client settings, then enable the system proxy. The client may override the port, so don’t rely solely on the value in the subscription file.
  4. Open the target site in a browser, then check the client’s connection log to confirm the matched rule, proxy group, and outbound route.

To set the GNOME proxy manually, go to Ubuntu Settings → Network → Network Proxy and select Manual. If the local mixed-port is 7890, enter 127.0.0.1 as the proxy host for both HTTP and HTTPS, and 7890 as the port for each. The HTTPS proxy field specifies the proxy endpoint for HTTPS requests; it does not mean the local port itself must provide TLS.

The desktop system proxy only affects applications that honor that setting. Terminal tools, containers, and some apps won’t automatically use GNOME’s proxy settings. The client’s configuration directory may not be ~/.config/mihomo: use its “Open Configuration Directory” option first, and distinguish the original subscription, merged template, and runtime configuration. Avoid editing files that will be overwritten the next time the subscription updates.

Command-Line Deployment: Binary and Directory Permissions

For a server setup, place the executable at /usr/local/bin/mihomo, the configuration at /etc/mihomo/config.yaml, and runtime data in /var/lib/mihomo. This layout makes write access easier to restrict and keeps the service’s working directory independent of the login user.

Install the Core and Create a Service Account

Obtain the official release binary built for your CPU architecture. If you downloaded a gzip archive, extract it first. The steps below assume the extracted file is at /tmp/mihomo. It must be an executable binary, not an archive or an HTML error page saved after a failed download. Don’t force a build with unsupported instruction sets onto an older CPU.

file /tmp/mihomo
sudo install -m 0755 /tmp/mihomo /usr/local/bin/mihomo
/usr/local/bin/mihomo -v

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

Run the account creation command only once. If an account with the same name already exists, check its purpose with id mihomo before changing the identity of an existing service. The configuration file should be owned by root and readable by the mihomo group. The service account needs write access to the runtime data directory for caches and any required rule or database files.

Create a Minimal Configuration That Starts

Run sudoedit /etc/mihomo/config.yaml and add the following. This only tests local listening and rule processing; all connections use DIRECT, so this setup provides no remote proxy. For actual use, replace it with a trusted, complete configuration containing real nodes, proxy groups, and rules—don’t just paste in a subscription URL.

mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
ipv6: false

rules:
  - MATCH,DIRECT
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

-f specifies the configuration file, -d sets the runtime data directory, and -t checks the configuration. Passing the check means the current core can parse the configuration; it does not prove that nodes are reachable or that rules behave as intended. If a complete configuration references external rule sets, Geo data, or local files, also check their paths, download requirements, and read permissions for the service account.

systemd Service File and Startup at Boot

Create the service with sudoedit /etc/systemd/system/mihomo.service. This setup is for a standard local proxy and does not grant permission to create network devices. Running under a dedicated account avoids using root just to listen on an unprivileged port such as 7890.

[Unit]
Description=mihomo proxy core
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=0027
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/mihomo

[Install]
WantedBy=multi-user.target

ProtectSystem=strict restricts writes to the filesystem, leaving write access only to the declared runtime data directory. Don’t put provider files that need ongoing updates under /etc/mihomo; use paths under /var/lib/mihomo in the configuration instead. How network-online.target waits depends on the system’s network management service; it does not guarantee that external subscription servers are reachable.

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'

Normally, the service status should be active (running), with a local proxy listening. If the logs show address already in use, first identify the desktop client or old process using the port. Don’t force-kill processes or switch to listening on all network interfaces as a first fix.

Test from the Terminal and Update the Configuration

curl --proxy http://127.0.0.1:7890 \
  --connect-timeout 10 --max-time 20 \
  -I https://example.com/

This command explicitly sets the proxy address, which helps distinguish “the application isn’t using the proxy” from “the proxy core failed to handle the request.” With this guide’s minimal configuration, requests still connect directly from the machine to the target site. After switching to a real configuration, check the logs to confirm the outbound route; an HTTP status code alone doesn’t prove that a node is working.

After editing the YAML, rerun the check with -t, then run sudo systemctl restart mihomo. This service has no ExecReload directive, so don’t assume systemctl reload will work. Run daemon-reload only after changing the unit or a drop-in. Restarting may interrupt existing connections, so choose an appropriate maintenance window on a server.

TUN Mode: Device Access, Capabilities, and DNS Routing

TUN uses a virtual network interface to handle traffic from applications that don’t honor system proxy settings. It is not another name for a regular HTTP proxy port, and it won’t automatically turn an Ubuntu server into a LAN gateway. Gateway forwarding also requires IP forwarding, firewall rules, and routing changes on other devices; those are outside the scope of local traffic capture.

Check That the TUN Device Is Available

ls -l /dev/net/tun
sudo modprobe tun

If the device is missing and loading the module fails, check whether the host provides TUN. Containers usually also need the device passed through from the host and network administration capabilities granted. Don’t make /dev/net/tun writable by everyone as a substitute for configuring service capabilities.

Grant Network Capabilities to a Non-Root Service

Run sudo systemctl edit mihomo and add the following drop-in. CAP_NET_ADMIN allows network interface and route operations; CAP_NET_RAW supports related raw socket operations. Grant these capabilities only to services that actually need TUN.

[Service]
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW

systemd grants these capabilities when the process starts, so there’s no need to run setcap on the binary. The base unit in this guide does not enable PrivateDevices=true. If you add device isolation or an allowlist for device access, make sure the service can still access /dev/net/tun.

Combine the TUN and DNS Configuration

Add the following fields at the root level of the existing YAML. If dns or tun is already present, merge these settings into the existing section; don’t define the same top-level key twice. Here, DNS listens on local port 1053 to avoid taking over the local DNS endpoint commonly used by systemd-resolved on Ubuntu.

dns:
  enable: true
  listen: 127.0.0.1:1053
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  nameserver:
    - 1.1.1.1

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53

auto-route requests automatic route configuration, while auto-detect-interface identifies the outbound interface automatically. The DNS example uses a public resolver; adjust it to match your network’s connectivity, as it is not a universal setting. fake-ip returns a virtual address for the core to map, not the target site’s real address.

dns-hijack handles DNS requests on port 53 that enter the relevant TUN path. It does not automatically detect and intercept encrypted DNS, such as an app’s built-in DoH. Enabling dns.enable alone does not mean all system DNS requests are routed through the core. Don’t delete /etc/resolv.conf or stop the system resolver to troubleshoot; first check the actual DNS and routing paths.

sudo -u mihomo /usr/local/bin/mihomo \
  -t -d /var/lib/mihomo -f /etc/mihomo/config.yaml
sudo systemctl daemon-reload
sudo systemctl restart mihomo
sudo journalctl -u mihomo -b -n 100 --no-pager
ip link show
ip rule show
resolvectl status

Automatic routing may affect your management connection when you’re connected to a remote server over SSH. Before enabling TUN, keep access to a cloud console or another out-of-band connection, back up the original configuration, and know how to stop the service. Don’t stack TUN from a desktop client, a standalone core, and another VPN’s default route.

Common Issues and Maintenance

Startup Failures: Check the Binary Before the Network

  • Exec format error: Usually caused by an architecture mismatch or trying to run an archive as a binary. Check the output of file and uname -m again.
  • Permission denied: Check access permissions on the configuration file and its parent directories. Success when running as your login user doesn’t mean the mihomo service account can also read the files.
  • Configuration loads, but rule sets fail: Check provider paths, network access, and write permissions for the data directory. When using ProtectHome=true, don’t reference subscription files in a user’s home directory.
  • TUN operation not permitted: Check, in order, that the device exists, the drop-in is loaded, and the runtime environment allows network administration capabilities. Run systemctl cat mihomo to view the merged unit.

Browser Works, but Terminal or Containers Don’t

This usually means the applications use different proxy settings, not that the core failed to start. For a single terminal command, set HTTPS_PROXY=http://127.0.0.1:7890, if the tool supports that environment variable. Using curl --proxy makes the proxy explicit and easier to test. In a container, 127.0.0.1 points to the container itself, not the Ubuntu host; you’ll need to configure a reachable address and access controls separately.

Keep allow-lan: false by default, and don’t expose the control interface to the public internet just to use a graphical dashboard. If remote management is necessary, use an authenticated method over a restricted network path and check the firewall. The HTTP proxy port and the control API port serve different purposes and are not interchangeable.

Upgrade and Stop the Service

Before upgrading, keep recoverable copies of the current binary and configuration. Stop the service before replacing the executable, then check the new version output, validate the configuration, and verify startup. Logs may contain target domains and connection details; remove subscription credentials and sensitive information before sharing them.

sudo systemctl stop mihomo
sudo systemctl disable mihomo
systemctl is-enabled mihomo

If the final command returns disabled, startup at boot has been disabled; the configuration file has not been deleted. If you previously set a manual GNOME proxy or terminal proxy variables, remove those too, or applications may keep connecting to the stopped 127.0.0.1:7890. On desktops, let the client manage system integration where possible. On servers, clearly defined file paths, service identities, and routing boundaries make problems easier to diagnose than repeatedly switching proxy modes.

Download Clash