First Clash-Compatible Client on NixOS: Flake, home-manager, and systemd User Service (2026)

NixOS is not “Linux with a weird package manager.” The whole system is a single declarative description: generations switch atomically, /nix/store is read-only, and “I edited /etc by hand and rebooted” is a smell rather than a victory. If you are trying to run a Clash-compatible stack—today most often Clash Meta (Mihomo)—that model changes where configs live, how you perform a subscription import, and how you wire systemd --user for login autostart. This guide is the Nix-shaped first setup page: a modern flake, optional home-manager for the user layer, a concrete systemd user unit that targets graphical-session.target, and the “store path versus mutable home” traps that make beginners think Clash is broken when Nix is just enforcing immutability.

How NixOS reframes the problem compared with Ubuntu, Fedora, or Arch

On a traditional distribution you download a .deb or AUR package, write YAML under ~/.config, and restart a daemon. On NixOS, the binary you run is still a normal program—often pkgs.mihomo or a similarly named Clash core in nixpkgs—but the file that references your provider may be produced by a module, a home-manager template, or a plain home.file you track in version control. That split is the whole point: the system stays reproducible, and your secrets and frequently rotated subscription URL stay outside the Nix store when you do things correctly.

Our Ubuntu 24.04, Fedora, Debian 12, and Arch Linux walkthroughs keep the “GUI-first” story, because that is what most desktop users search for. This article assumes you are comfortable reading Nix expressions and know what a flake is; if the vocabulary is new, work through the official Nix manual and a short flake tutorial first, then return here. While you are still mapping names to roles, read Clash ecosystem in 2026: which projects are still maintained so you understand why Mihomo shows up in so many rules and subscription templates; if rule grammar still feels foreign, the Clash tutorial on this site walks through outbounds, groups, and listeners with the same mental model the YAML uses.

Prerequisites: flakes enabled and a user session that can run a user manager

Enable experimental flake support and the unified CLI as documented for your release channel, pin nixpkgs to a revision you can reproduce, and keep the machine clock sane—TLS to subscription endpoints fails in confusing ways if time drifts, exactly like on any other Linux distribution. You need a non-root Nix user (or Home Manager) that can start systemd --user units after graphical login, because most readers want a desktop session where browsers and IDEs start after the proxy is already listening.

Kernel capabilities for TUN and lower-level transparent modes are a separate contract from “run Mihomo in userspace on the mixed port.” This guide focuses on a headless or tray-light core with correct ordering under graphical-session.target, not on packaging every Clash-branded GUI; many GUIs on NixOS are community modules or buildFHSEnv wrappers, and their names move faster than a single blog paragraph should pretend to pin. The durable pattern is: declare the core in environment.systemPackages or home.packages, point it at a mutable config directory, autostart with a user unit.

Pick the Clash-class core: mihomo in nixpkgs

The Nix package you want is usually the maintained Clash Meta implementation shipped as pkgs.mihomo (naming in nixpkgs can evolve—search your pinned channel for “mihomo” or “clash-meta”). The binary path you care about in units is the store path, which changes when you nixos-rebuild or home-manager switch; you must never hardcode yesterday’s /nix/store/…-mihomo-…/bin/mihomo in a hand-edited file outside the Nix module system. The module already resolves ${pkgs.mihomo}/bin/mihomo at build time, which is the entire reason we tolerate verbose configuration.

If you track everything in a flake, add nixpkgs as an input, expose pkgs for your NixOS and home-manager modules, and keep a single lock file so a coworker can reproduce your desktop stack. That discipline matters when you are not “installing a random AppImage from a forum” but importing a subscription that must stay in sync across laptops.

💡 GUI optional If you need a full graphical Clash Verge–class experience on NixOS, look for a maintained NixOS module, overlay, or upstream flake, then treat the GUI the same as any other Electron or Qt program: it still talks to a Mihomo core that expects profiles under your home. This page privileges the service-shaped setup because it is what searchers mean when they type systemd --user with NixOS and Clash.

Where the configuration file actually lives: never the store for day-to-day edits

/nix/store is immutable. If you materialize a config.yaml with pkgs.writeText and that file contains your rotating subscription query parameters, the next Nix rebuild rewrites the path and your provider URL looks “stuck.” The usual fix is: keep generated defaults in the store if you like, but place machine-specific overrides and the provider URL in a mutable path such as ~/.config/mihomo (exact directory names follow upstream docs), and either symlink from home.file or use an activation script. For secrets, prefer sops-nix, agenix, or another pattern that decrypts at activation time; never commit raw subscription tokens to a public flake on GitHub.

home-manager shines here: you describe xdg.configFile entries or a whole directory, roll forward with home-manager switch, and still keep secrets out of the world-readable store. If you are only experimenting, a plain mkdir -p ~/.config/mihomo and a hand-pasted config.yaml is fine—Nix does not require YAML to be pure if you are honest about which parts are mutable.

When a provider’s dashboard gives you a long HTTPS subscription import link, you paste it the same way you would on Debian or Arch: the difference is only where the resulting downloaded profile lands on disk, not the semantics of import. If the URL throttles or returns HTTP 429, the discussion belongs in subscription links for Clash: why they expire and how to refresh, not in journalctl—though journalctl --user -u mihomo.service -e is still the right place to read fetch errors after you add the unit below.

Compose home-manager: user packages, XDG, and a user systemd service

Import Home Manager in your flake and wire home-manager.users.yourname to a module that sets home.packages = [ pkgs.mihomo ]; at minimum. Then declare systemd.user.services.mihomo (service names are yours to choose) with Unit blocks that order after the session is real: After=graphical-session-pre.target and PartOf=graphical-session.target are common choices so the proxy does not start before a desktop DISPLAY or Wayland environment exists when you are running a tool that still expects one.

ExecStart should invoke the Nix store binary with a directory flag your core documents—illustrative pattern:

ExecStart=%h/.nix-profile/bin/mihomo -d %h/.config/mihomo

Replace that with ${pkgs.mihomo}/bin/mihomo in the module literal so you never rely on a stale ~/.nix-profile if you are not using profiles that way, and set -d to the directory you actually use. Restart=on-failure and a reasonable RestartSec= pair keep the service from flapping when Wi-Fi is still associating. Enable with systemd.user.services.mihomo.wantedBy = [ "graphical-session.target" ]; in Home Manager, then home-manager switch and systemctl --user daemon-reload if you touched units manually. Start it interactively the first time with systemctl --user start mihomo.service and read journalctl --user -u mihomo.service -b for a clean baseline.

Because this is a systemd --user service, it stops when you log out unless you have enabled lingering with loginctl enable-linger (usually unnecessary on a personal laptop, and a security review topic on shared servers). That behavior matches the mental model in our Debian and Arch articles: the user service is tied to your session, not to multi-user systemctl without a login.

System-level NixOS options versus pure Home Manager

Some users prefer environment.systemPackages in configuration.nix and a global template for the daemon. That is valid when a machine is single-user. If several people share a workstation, keep each profile in each user’s home-manager file and avoid a root-owned Clash config. nixos-rebuild switch will still restart or reload units that the module system wires; check your exact module to see whether a systemd.user service is reloaded automatically when you switch—when in doubt, restart the user service once after a channel bump because the store path to Mihomo changes.

Updating: channels, flakes, and “why did my port change?”

On NixOS, you update the system by changing inputs and rebuilding, not by apt upgrade alone. When nixpkgs advances, Mihomo may gain features or change defaults that interact with fake-ip or DNS listeners—exactly the class of change we document for sniffing in Clash Meta sniffing and rule exceptions, not a “Linux bug.” Read upstream release notes when you roll forward, and keep a short note in your flake commit message describing why you changed a DNS or TUN option.

Port numbers are not a religion: confirm the mixed port the running core binds to before you point ALL_PROXY at a folklore value. If you are comparing transparent modes, TUN mode explained is still the same conceptual content as on other distributions, but capability boundaries on NixOS may add polkit or device-node details your profile must reference honestly.

Validation checklist before you open an issue or rewrite your flake

Walk through the same “disciplined” sequence we use on other Linux guides, adapted for immutable paths: confirm one owner binds the listen ports, verify systemctl --user is-active mihomo.service after login, run a small curl -x or browser test against the documented mixed port, and watch whether subscription fetch times advance in the journal when you expect periodic updates. If you split traffic by rules, the custom rules tutorial and proxy-groups articles stay authoritative for grammar; Nix does not make match ordering less important.

  • Store vs home: if you can only edit a file with sudo, you are probably fighting the store—relocate the mutable bits.
  • Path churn: after every rebuild, the mihomo string in readlink -f $(which mihomo) can change; modules hide that for you, manual units do not.
  • Session timing: a unit that runs “too early” in login lacks environment variables GUI tools expect; attach to the graphical session or source a small wrapper.
  • Secret hygiene: import links belong in a secrets mechanism or a private file, not a public flake.

Why this topic deserves its own page next to the Debian and Arch guides

Search intent for “Clash on Linux” is saturated with generic one-liners, but NixOS users type different queries: flake inputs, nixos-option discoveries, and home-manager activation scripts. A tutorial that only talks about apt or the AUR will waste their time, while a tutorial that only talks about Nix abstractions will waste everyone else’s. This page is the bridge: it keeps the Clash Meta (Mihomo) subscription model, points to the same ecosystem article as the rest of the site, and names the systemd --user target relationship explicitly so the service graph is legible in journalctl instead of in tribal knowledge.

Compared to opaque installers, a declarative proxy stack is easier to review: you can read the module, you know which nixpkgs commit produced the Mihomo binary, and you can roll back a generation if a provider-side subscription import change interacts badly with your rules. When you are ready to line up the client artifacts with the same place we use for Windows and macOS documentation, the site’s download page still anchors “which GUI builds exist this month” even if your Nix build comes straight from the cache.

Closing: declarative first, mutable where it must be, autostart you can read

NixOS rewards treating Clash-compatible cores as normal packages, keeping subscription material out of the store, and expressing autostart as a systemd --user unit that follows your graphical session. The workflow does not remove the need to understand rules and DNS: it just stops you from debugging five layers of apt at the same time as you debug routing. If you are comparing approaches across distributions, the Debian and Arch pages remain useful references for what the GUI and port expectations look like when you are not in a pure flake world yet.

When the pieces line up, you get reproducible updates, a single command to return to a known generation, and logs that make sense. For the last mile—choosing a maintained client build across platforms and keeping installers aligned with what we document in tutorials—lean on the same curated entry as other readers: our download pageDownload Clash for free and experience the difference.