Install Mihomo (Meta) on CentOS Stream 9 / RHEL 9: Binary Deploy and systemd Autostart

Search queries that mention CentOS Stream 9, RHEL 9, Mihomo, Clash Meta, and “Linux install systemd” almost always assume a headless server: no Clash GUI, remote SSH only, SELinux enforcing, possibly firewalld with default-deny ingress. Debian- and Ubuntu-oriented guides rarely spell out Enterprise Linux quirks in one place—and Fedora desktop articles cover Verge tray icons, not a stable path under /etc. This article is the complementary RHEL-shaped walkthrough for 2026 operators: fetch the upstream release binary for your ABI slice, settle configuration under /etc/mihomo, attach your provider’s HTTPS subscription URL through proxy-providers, reconcile SELinux labels when something refuses to exec, tighten firewalld holes only where you deliberately expose LAN access, wrap it in a hardened systemd service that survives reboot and journalizes failures cleanly, then prove the stack with curls and concise log checks—not guesswork copied from workstation blogs.

Why Enterprise Linux warrants its own Mihomo playbook

RHEL-family hosts show up continuously in procurement stories: virtualization clusters, bastion relays, QA farms, observability subnets. Those readers do not paste .deb instructions from Ubuntu 24.04; they inspect aide baselines and keep SELinux in enforcing unless an RFC says otherwise. A binary dropped into ~/Downloads without labels or ownership is unacceptable on audited machines, whereas the same tarball on laptop Arch might “just run.” Aligning Mihomo—a maintained Clash Meta successor core—with RHEL ergonomics removes that tension: predictable paths (/usr/local/bin, /etc/mihomo), systemd-native supervision, firewall zones you can diff in Git, logs that auditors already ingest from journald, and reproducible artifact verification against upstream checksums posted beside each release artifact.

If you landed here after trying our Clash Verge on Fedora SELinux guide, treat that document as sibling context for graphical Workstation quirks and polkit dialogs. Fedora’s toolchain shares plenty with EL9 kernels, yet this page focuses purely on unattended services: dedicated OS user accounts, listeners bound only on addresses you intentionally expose in YAML, firewall ports opened with explicit rationales—not “disable firewalld to finish the demo.” Operators still choosing between maintained clients should skim Clash ecosystem in 2026; if rule vocabulary (“profile,” “MATCH,” outbound selection) feels alien, reconcile terms through the site Clash tutorial before you rewrite YAML blindly.

Nomenclature: Mihomo binaries distributed by MetaCubeX implement the evolving Clash Meta YAML surface most modern subscription templates target. Mentioning both names satisfies search variants without implying two separate forks; think “upstream core name Mihomo,” “upstream rules dialect Clash Meta,” “GUI clients optionally wrap similar cores.” Servers generally skip GUIs outright and talk to the YAML file directly—which is precisely what frees you from X11 entropy when you troubleshoot at 03:00 from a cramped jump host.

Prerequisites, compliance hygiene, and what you refuse to shortcut

Create the dedicated daemon account before unpacking configuration (ordering matters for chown on fresh directories):

sudo useradd --system --home /var/lib/mihomo --shell /sbin/nologin --create-home mihomo

Operate as a sudo-capable admin with working outbound HTTPS to fetch release artifacts unless an internal artifact mirror substitutes that step. Maintain accurate time synchronization (chronyd on EL9 is standard): TLS handshake failures mimic “bad subscription URLs” beautifully when clocks skew minutes off NTP anchors. Decide whether Mihomo terminates user traffic strictly on-loopback—for example SSH dynamic forwarding or local containers—or whether other LAN hosts consume the mixed SOCKS/HTTP listeners. LAN exposure increases attack surface radically; firewall rules should mention exact ports sourced from configuration, never cargo-cult constants like assuming 7890 without reading YAML.

Capture your provider HTTPS subscription link before editing files. If rotations, expirations, or HTTP 429 throttling confuse you, read subscription links for Clash; nothing in systemd or Yum fixes a dead URL. Decide whether ephemeral nodes must auto-refresh hourly or nightly—heavy polling annoys dashboards and wastes bandwidth inside metered egress facilities.

💡 Run as a dedicated user Create mihomo (nologin shell acceptable) owning /etc/mihomo writable subtrees (/var/lib/mihomo alternatively) so systemd never executes the daemon as uid 0. Root-only configs often survive lab tests yet fail audits and leak provider secrets into sloppy backup scripts.

Architecture choice: amd64-compatible versus amd64-v3

Mihomo’s release matrices ship multiple amd64 binaries: a baseline “compatible” build and accelerated builds targeting CPUs with BMI2, LZCNT, MOVBE, POPCNT (upstream labels vary—consult the tarball names on the release notes page you trust). Probe generically with:

grep -m1 '^model name' /proc/cpuinfo
lscpu | grep -i flags

If procurement handed you Ivy Bridge-era VMs, grab the conservative artifact; crashing immediately with illegal instruction screams “grabbed the fast slice by mistake.” aarch64 gateways—some cloud shapes and Raspberry-class lab boxes—want the linux-arm64 asset instead.

Unpack the gzip release and install into /usr/local/bin

Assume you fetched mihomo-linux-amd64-compatible-<VERSION>.gz into /tmp; replace placeholders with semantic versions you audited (verify upstream checksum manifests when your change board demands it).

cd /tmp
gunzip -c mihomo-linux-amd64-compatible-*.gz | sudo tee /usr/local/bin/mihomo >/dev/null
sudo chmod 755 /usr/local/bin/mihomo
sudo restorecon -vF /usr/local/bin/mihomo || true

If restorecon is unavailable outside full policy tooling, SELinux denial symptoms show up instantly when launching via systemd; defer to section below for targeted chcon or persisted semanage fcontext. Confirm version output matches expectations:

/usr/local/bin/mihomo -v

Keep yum/dnf from shadowing manual installs accidentally (none should provide mihomo today, but pinning documents helps future mergers). Updating later means overwriting the binary during a deliberate maintenance window, restarting systemd, briefly diffing changelog notes for breaking YAML directives—same discipline you apply when bumping Envoy sidecars.

Filesystem layout under /etc/mihomo

Centralize authoritative YAML at /etc/mihomo/config.yaml. Place downloaded provider bundles under something like /etc/mihomo/providers/; ensure SELinux recognizes content under etc_t or the specific type your policy expects (some teams relocate runtime caches entirely to /var/lib/mihomo/). Example skeleton permissions:

sudo install -o mihomo -g mihomo -d /etc/mihomo/providers
sudo install -o mihomo -g mihomo -m 640 /path/to/generated/config.yaml /etc/mihomo/config.yaml
sudo chown -R mihomo:mihomo /etc/mihomo
sudo chmod 750 /etc/mihomo

Harden confidentiality: YAML often embeds API tokens disguised inside provider URLs—treat backups and config management repos like credential stores. Separate checkouts for infra versus application teams reduce accidental Slack leak surface.

Wire HTTPS subscription fetching with proxy-providers

Rather than dumping raw node lists manually, Mihomo excels at recurring remote templates. Populate proxy-providers: with your provider HTTPS URL, sane interval, and deterministic path. Example structure (adapt names & groups to vendor templates—you must merge GEOIP/rules tail consistent with organizational policy):

# /etc/mihomo/config.yaml (illustrative)
mixed-port: 7890

mode: rule
log-level: info
allow-lan: false

proxy-providers:
  remote-sub:
    type: http
    url: https://REPLACE.example/subscription-token
    path: ./providers/remote.yaml
    interval: 7200

proxy-groups:
  - name: SEL
    type: select
    use:
      - remote-sub

rules:
  - MATCH,SEL

Why proxy-providers dominate headless deployments: scheduled refresh without brittle cron wrappers, hashed files you can checksum, reproducible outages when dashboards show fetch failures keyed to systemd timestamps. Tune interval thoughtfully—five-minute scraping rarely helps unless operations actively validate capacity; nightly or bi-hourly often suffices unless compliance demands faster failover.

After first successful ingest, peek at rendered provider caches to ensure nodes surfaced as expected (/etc/mihomo/providers/). If GEOIP RULE-SETS require separate downloads under rule-providers blocks, bake those sections before marking production ready—a half-built ruleset looks “green” locally yet routes everything oddly once MATCH fires.

DNS deserves explicit mention: systemd-resolved and fake-ip interplay remain the leading stealth failure class on systemd distributions. Operators mixing OS stub resolvers with Mihomo inbound DNS should revisit Linux Clash and systemd-resolved DNS troubleshooting because “CentOS hates proxy” narratives often distill to contradictory resolver layering.

systemd: system unit for production headless relays

Create /etc/systemd/system/mihomo.service emphasizing least privilege paths and restart hygiene:

# /etc/systemd/system/mihomo.service
[Unit]
Description=Mihomo (Clash Meta) daemon
Documentation=https://wiki.metacubex.one/

After=network-online.target nss-lookup.target
Wants=network-online.target

[Service]
User=mihomo
Group=mihomo
NoNewPrivileges=true
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
Restart=on-failure
RestartSec=5
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target

For listeners on ports below 1024 (uncommon for Mihomo’s default mixed port) or for extra-hardening variants, add narrowly scoped AmbientCapabilities after change review. Transparent TUN mode typically demands CAP_NET_ADMIN and kernel module availability—extend the unit only after you consciously enable TUN in YAML and accept the risk; until then the template above matches a typical high-port HTTP/SOCKS relay without capability elevation.

sudo systemctl daemon-reload
sudo systemctl enable --now mihomo.service
sudo journalctl -u mihomo -f

Logs should show inbound provider fetch completions and listener binds; repeated crash loops merit diffing SELinux AVC lines (ausearch -m avc) before lazily widening capabilities. Operators habituated to Debian systemd user units from Clash Verge on Debian 12 should consciously switch mental model—these services start at multi-user boundary without graphical-session targets cluttering graphs.

SELinux: evidence before blindly permissive

When execstart denies, gather kernel audit lines tying domain transitions to binaries under /usr/local/bin or writable paths under etc; map them to targeted file contexts. Quick triage includes ls -Z /usr/local/bin/mihomo and comparing with known-good daemons. Corporate environments often standardize custom modules—coordinate with your platform team instead of shipping ad-hoc audit2allow dumps from internet forums. Denials referencing unexpected types on provider YAML indicate you dropped caches into mislabeled directories; restorecon -RFv /etc/mihomo often repairs mislabeling after manual copies.

firewalld and LAN exposure when allow-lan flips true

Loopback-only deployments skip inbound holes—localhost traffic bypasses most public zone concerns. If you intentionally set allow-lan: true so lab devices hit 192.168.x.y:7890, open only that TCP port (and UDP if your profile demands it) on the interface’s active zone, persist with --permanent, then firewall-cmd --reload. Document each addition in change tickets; future you should know why port 7890 appeared in public versus internal. Cross-read Windows LAN proxy firewall patterns for conceptual parity even though tooling diverges—both stories revolve around scoped inbound allowance instead of carpet-bombing rule removal.

Verification you can script in five minutes

  • Listener truth: ss -lntp | grep mihomo should show expected mixed or split ports from YAML without duplicate orphan processes.
  • Outbound path: curl -I --proxy http://127.0.0.1:7890 https://example.com (adjust port) proves application-layer routing; swap host for remote curl when testing LAN mode.
  • Fetch cadence: watch logs during two interval cycles to ensure providers refresh without HTTP 401/403/429 escalations.
  • Failure injection: temporarily break DNS and confirm restart policy plus log clarity—playbooks should mention triage not mysticism.
  • Upgrade rehearsal: replace binary, systemctl restart mihomo, confirm zero-downtime expectations against service-level objectives.

Operations after day zero: updates, backups, observability

Track upstream security advisories; Mihomo’s surface touches TLS fetchers, rule providers, and optional GeoIP databases—each path is an update channel. Mirror artifacts internally if air-gapped regulations forbid direct GitHub egress. Hash verification plus reproducible Ansible roles keep fleet drift visible. Export metrics if your platform demands them (health-check logs, connection counts) through sidecars or log scrapers—systemd already timestamps events with monotonic ordering friendly to Loki stacks.

When teams request GUI parity for incident responders, note that Clash Verge Rev and similar clients layer atop the same core yet assume interactive sessions; bridging that gap on servers usually means YAAML Git reviews plus optional web dashboards, not X11 forwarding fragile sessions. Cross-platform standardization for humans who also carry laptops still benefits from this site’s Download Clash hub so everyone chases consistent major versions.

Troubleshooting shapes specific to EL9 services

Service starts then dies: parse first exception lines in journalctl -u mihomo -b; YAML syntax errors or missing provider files often appear before network noise. Fetch succeeds but no nodes: confirm provider format compatibility with Clash Meta dialect—legacy Premium-only keys still appear in some enterprise contracts; see Clash Meta vs Premium for capability deltas. Listeners missing although process runs: capability sets may block privileged ports under 1024—either raise caps deliberately or move listeners above that threshold. Intermittent DNS: revisit resolved alignment and avoid double fake-ip stacks.

Frequently asked questions

Does CentOS Stream 9 differ materially from RHEL 9 for this guide?

Operationally the steps align: same major systemd, firewalld, SELinux defaults, similar kernel toolchains. Stream tracks ahead of point RHEL cadence; validate kernel module requirements if you enable advanced features (EBPF hooks, TUN offloads). Subscription-based RHEL may mandate vendor support statements before running community binaries—document risk acceptance accordingly.

Why avoid running Mihomo as root long term?

Beyond audit findings, root-owned YAML encourages sloppy secret sharing and complicates least-privilege container sidecars. Dedicated service users isolate compromise blast radius and align with DISA STIG-style guidance about daemon accounts.

Can I containerize instead?

Often yes, yet this article targets bare-metal clarity for operators who still standardize on systemd unit files. If your platform mandates Podman, map capabilities, volume mounts for /etc/mihomo, and network namespaces deliberately—half-mapped bind mounts produce some of the hardest DNS bugs to explain in postmortems.

Headless Meta versus black-box VPN appliances

Many traditional VPN clients on Linux servers hide routing behind opaque helper binaries: you get a single toggle, minimal visibility into which process opened which socket, and log lines that marketing teams wrote instead of SREs. That opacity becomes expensive when a compliance auditor asks why traffic to a sensitive API subnet detoured through an unexpected region. Mihomo under explicit YAML forces you to name groups, rules, and fetch intervals—more homework on day one, far faster root-cause isolation when something drifts after a provider reshapes node names overnight.

Clash-family tooling on desktops often pairs that same core with polished clients like Clash Verge Rev, which trade terminal editing for visual diffing—ideal for humans who live in GUIs. On CentOS Stream 9 or RHEL 9 bastions, the win is identical transparency without surrendering Enterprise Linux hygiene: SELinux-evidence-backed exec paths, systemd restart semantics, narrowly opened firewalld ports, reproducible binaries, and YAML you can stash in Git with review workflows your security team already understands. Many teams prototype rules on workstations, then lift the matured profile onto these headless relays during change windows—which is smoother when everybody references the same maintained ecosystem map and downloads from one curated entry point rather than disparate mirrors.

Compared with heavyweight closed appliances that postpone updates until quarterly maintenance, an open YAML-driven core keeps pacing with upstream CVE fixes when your process permits—while still outperforming improvised shell scripts dangling corkscrew chains nobody documents. Once you internalize fetch logs, outbound selection, and port inventory as first-class telemetry, diagnosing “subscription looks fine yet curl dies” collapses into a short checklist rather than a panicked distro reinstall. Standardizing interactive installs for colleagues across macOS or Windows—including official clients from our download hub—keeps terminology aligned when escalation bridges server and laptop teams reviewing the same profile diff. If your next step involves distributing a maintained GUI build alongside these daemons for mixed fleets, grab current artifacts from our download page and download Clash to experience transparent routing firsthand where a visual client actually helps day-to-day operators.