Tutorial 2026-04-05 · About 14 min read

Clash Meta on Linux Headless: Install, Systemd Service, and Auto Subscription Updates

Windows and Android guides assume a GUI. On a VPS or a home server without a desktop, you run the core directly: a single long-lived process, YAML configuration, and a supervisor that restarts it when things go wrong. This article walks through the Clash Meta-class stack many people install via the mihomo release, covering binary placement, proxy-providers with periodic fetch, a production-style systemd unit, boot ordering, CLI proxy settings for tools like curl and git, and how to reload configuration without unnecessary downtime. If you are building a side-router or remote dev box, this is the workflow that actually matches your environment.

Why Clash Meta on headless Linux

Headless servers have no tray icon and no profile wizard. Every behavior must be expressed in configuration files and enforced by the init system. The Meta family of cores tracks features and rule ecosystems that many providers expect today, which reduces friction when you copy patterns between a laptop client and a datacenter relay. Architecturally, Clash listens on local ports (often a mixed-port for HTTP and SOCKS), applies routing rules, and forwards flows to outbound proxies. The external controller exposes a REST API so automation can query state, swap profiles, or request a reload—essential when SSH is your only UI.

Compared with desktop tutorials, Linux server work adds three recurring responsibilities. First, you must ship an executable that matches the machine architecture and keep it updated when protocols change. Second, directory permissions should follow the principle of least privilege: run as a dedicated account, keep secrets out of world-readable paths, and separate writable provider caches from static config. Third, integrate with systemd so crashes become transient: exponential backoff restarts, explicit dependencies on network readiness, and observability through journalctl. Once those pieces exist, layering transparent forwarding or TUN-style capture becomes an incremental project instead of a fragile one-off.

Prerequisites: user accounts, ports, and cold-start networking

Checklist

  • Architecture: confirm with uname -m and download the matching release asset (amd64, arm64, etc.).
  • Service user: create something like clash or mihomo; own the config tree with that user.
  • Port plan: reserve mixed or split HTTP/SOCKS ports plus external-controller; firewall the controller away from the public internet.
  • Subscription reachability: if the subscription URL only works through an existing proxy, plan a cold-start path (seeded provider files or staging on another host).

Side-router readers may combine this guide with kernel forwarding, nftables, or DHCP tricks. This page stops at a solid local egress: reliable upstream SOCKS/HTTP on the box itself. Gateway integration is easier when the core already runs under systemd with predictable paths and logs.

Install the mihomo (Clash Meta) binary

Grab the current release from the project’s GitHub Releases page, pick the archive or single binary for your architecture, and install it to a standard location such as /usr/local/bin/mihomo. Verify checksums when the release publishes them. Distribution packages or AUR helpers are fine if they track recent versions; stale builds may reject newer YAML keys and exit immediately, which looks like a mysterious service failure in logs.

  1. Match uname -m output to the filename in the release assets list.
  2. Verify integrity using the published digest when available.
  3. Install with sudo install -m 0755 mihomo /usr/local/bin/mihomo or equivalent.
  4. Run mihomo -v as the service user to confirm execution and library compatibility.

Tip

Prefer ReadWritePaths in systemd plus a dedicated user over running as root and opening permissions broadly. The latter pattern leaks subscription URLs to any local account.

Configuration directory and minimal skeleton

A common layout places the active config.yaml under /etc/mihomo (or /var/lib/mihomo) with consistent ownership. The main file declares listeners, mode, logging, controller binding, and which proxy-providers or rule-providers to load. On servers, always set a non-empty secret for the external controller and bind it to 127.0.0.1 unless you fully understand the exposure model.

The skeleton below is intentionally small: extend it with real policy groups, parsers, and rules before production use. Replace the sample URL with your provider link.

port: 7890
socks-port: 7891
mixed-port: 7893
mode: rule
log-level: info
external-controller: 127.0.0.1:9090
secret: "change-me"

proxy-providers:
  airport:
    type: http
    url: "https://example.com/subscription"
    path: ./providers/airport.yaml
    interval: 3600
    health-check:
      enable: true
      interval: 600
      url: https://www.gstatic.com/generate_204

proxies: []

proxy-groups:
  - name: PROXY
    type: select
    use:
      - airport

rules:
  - MATCH,PROXY

The interval field controls how often the core re-downloads the subscription and merges proxies into memory. Health checks actively probe nodes so manual rotation happens less often. If your provider returns a full profile instead of a provider fragment, adapt the structure per the documentation for your exact version—field names evolve, and strict parsers fail closed.

Authoring the systemd unit

Create /etc/systemd/system/mihomo.service. Use Type=simple, point WorkingDirectory at the config root, and invoke mihomo -d /path. Enable Restart=on-failure with a sane RestartSec, and raise LimitNOFILE for busy relays. If PPPoE or VPN links flap, align After= with network-online.target and ensure your distro actually waits for routable connectivity, or the first fetch may race and leave providers empty.

[Unit]
Description=mihomo (Clash Meta) daemon
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=mihomo
Group=mihomo
WorkingDirectory=/etc/mihomo
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
Restart=on-failure
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
  1. sudo systemctl daemon-reload
  2. sudo systemctl enable --now mihomo.service
  3. systemctl status mihomo should show active (running)
  4. Use journalctl -u mihomo -e for YAML errors, DNS failures, or port collisions

Security

An exposed external-controller without firewalling and a strong secret is equivalent to publishing a remote control for your proxy policy. Default to loopback and manage through SSH port forwarding when you administer from another machine.

CLI and automation through environment variables

Once mixed-port listens locally, many tools honor http_proxy, https_proxy, and all_proxy. This is the lowest-friction path for curl, wget, numerous language package managers, and CI jobs that already support standard variables. Example exports (adjust the port to your file):

export http_proxy="http://127.0.0.1:7893/"
export https_proxy="http://127.0.0.1:7893/"
export all_proxy="socks5://127.0.0.1:7893/"

Some utilities only read uppercase names; others only lowercase. Scope variables per shell session or per command (env http_proxy=... curl ...) instead of baking them into global profiles if maintenance traffic should stay direct. Applications that ignore proxies entirely may need proxychains, redsocks, or a TUN capture layer—each adds operational surface area, so reach for them when plain env vars are insufficient.

Tip

When enabling fake-ip or other DNS-heavy modes, read the version-specific notes. Misconfigured DNS can make local services resolve to placeholder addresses and break unrelated daemons on the same host.

Subscription refresh and configuration reload

Provider downloads on an interval typically update nodes without restarting the whole process. Editing config.yaml itself—adding providers, changing rule files, or altering listeners—usually requires a full reload. Preferred pattern: call the controller API with the bearer secret (exact paths vary by release; common patterns target /configs with PUT). A blunt alternative is systemctl restart mihomo, which is simple but creates a short outage window.

Optional systemd timers can run a small curl script hourly to force a refresh when your provider changes endpoints unpredictably and you do not want to shorten every interval. Store secrets in an EnvironmentFile with mode 600 and reference it from the unit; never world-read helper scripts that embed tokens.

Observability matters on servers: pair structured logs with simple probes—HTTP GET against the controller health route if available, or a timed curl through the mixed port—to catch silent provider failure before your automation stack does.

FAQ

The service exits immediately with “address already in use”

Inspect listeners with ss -lntp or lsof -i :7893. A leftover manual process or another proxy may hold the mixed or controller port. Standardize on systemd as the only supervisor and stop duplicate foreground runs.

Subscription downloads always fail

Debug with curl -v from the same host to separate DNS, TLS, and path issues. If the subscription requires an already-working proxy, seed initial providers files from a trusted machine or temporarily fetch on a network that reaches the URL, then rely on scheduled updates once nodes work. Trying alternate provider hostnames often fixes cold-start more than cranking interval blindly.

Permission errors writing provider caches

Ensure the runtime user matches directory ownership and that ./providers exists beneath WorkingDirectory. Running from a root-owned tree with a non-root service user is a frequent mistake right after copying sample configs from documentation sites.

Summary checklist

  1. Install the correct architecture binary under a controlled path and verify the version string.
  2. Author config.yaml with proxy-providers, sane intervals, and optional health checks.
  3. Ship a systemd unit with restart policy, file limits, and realistic network dependencies.
  4. Expose mixed-port locally; export proxy environment variables for CLI workflows; keep the controller on loopback.
  5. Use API reload or controlled restarts for structural changes; let intervals handle routine node list updates.

Want a polished desktop client too?

Servers stay on YAML and systemd; workstations benefit from GUI clients with import helpers. Grab an integrated build from our download page if you also need a user-friendly experience on Windows or macOS while this headless instance acts as upstream or gateway.

Download Clash for free and get started with a smoother setup

Stable headless core, friendly desktop option

Run Meta on Linux for always-on routing; use the curated GUI build where clicking beats editing YAML.

Download Clash