2.home-manager/docs/manual/usage/modular-services.md
cinereal fb6a0c6d39 modules: add modular services support
Adds `home.services`, an attribute set of nixpkgs
[modular services](https://nixos.org/manual/nixos/unstable/#modular-services)
sourced from `<nixpkgs/lib/services/lib.nix>`. Each service exposes
`process.argv` and the upstream NixOS-style systemd schema
(`systemd.lib`, `systemd.mainExecStart`, `systemd.service`,
`systemd.services`, `systemd.sockets`) by re-exporting
`nixos/modules/system/service/systemd/service.nix`. Service modules
shipped with `_class = "service"` (e.g.
`pkgs.<name>.passthru.services.default`) drop in unchanged --
service portability across module systems is the point of modular
services.

Lifted units are evaluated and translated from NixOS-style attrs
(`wantedBy`, `serviceConfig`, `unitConfig`, `environment`, ...) into
the section-based INI shape (`{ Unit; Service; Install; }`) that
home-manager's `systemd.user.{services,sockets}` consumes; only the
common keys are mapped, uncommon options remain reachable via
`unitConfig` / `serviceConfig` / `socketConfig`. Sub-services and
their units are dashed under the parent service name;
`process.argv` becomes the default `ExecStart` for the service's
primary unit, which defaults to `WantedBy=default.target`.

Mirrors the surface of nixpkgs' portable systemd module (services +
sockets only); other unit kinds home-manager supports natively
(timers etc.) are intentionally not modeled until upstream grows
them.

Each service's `configData.<name>` entries are materialized at
`$XDG_CONFIG_HOME/system-services/<service-prefix>/<name>` (mirroring
how `nixos/modules/system/service/systemd/{config-data-path,system}.nix`
lifts `configData` to `environment.etc`), with the absolute path
injected back into `configData.<name>.path` so the service can refer
to its files at a stable location.

Includes nmt tests covering: a basic `process.argv`-only service, a
service with a `configData` entry, and importing
`pkgs.ghostunnel.passthru.services.default` to assert the lifted user
unit contains the expected ExecStart flags and `LoadCredential`
entries.
2026-05-04 17:22:16 +02:00

3 KiB

Modular Services

Home Manager supports nixpkgs modular services under . This is the home-manager analog to the NixOS system.services namespace: each entry is an abstract service sourced from <nixpkgs/lib/services/lib.nix> with the upstream portable systemd module loaded into it, so service modules shipped with packages (e.g. pkgs.<name>.passthru.services.default) drop in unchanged -- the same module evaluates on NixOS and on Home Manager.

A minimal example -- run a one-shot user service from a package's modular service definition:

{ pkgs, ... }: {
  home.services.tunnel = {
    imports = [ pkgs.ghostunnel.passthru.services.default ];
    ghostunnel = {
      listen = "127.0.0.1:8443";
      target = "127.0.0.1:8080";
      cert = "/run/secrets/cert.pem";
      key = "/run/secrets/key.pem";
      allowAll = true;
    };
  };
}

This produces ~/.config/systemd/user/tunnel.service with the expected ExecStart, LoadCredential, and WantedBy=default.target.

Each service exposes the upstream NixOS-style schema: process.argv, systemd.lib, systemd.mainExecStart, systemd.service, systemd.services, systemd.sockets. Lifted units are translated from NixOS-style attrs (wantedBy, serviceConfig, unitConfig, environment, ...) into the section-based INI shape ({ Unit; Service; Install; }) that home-manager's consumes. Only common keys are mapped explicitly; uncommon options remain reachable via unitConfig, serviceConfig, or socketConfig.

Sub-services (nested services.<sub> inside another service) and their units are dashed under the parent service name. The empty unit key "" denotes the service's primary unit (lifted to a unit named after the service itself); process.argv becomes the default ExecStart for that unit, which defaults to WantedBy=default.target.

Configuration data

Each service can declare configuration files via configData.<name>. These are materialized at $XDG_CONFIG_HOME/system-services/<service>/<name> (mirroring how NixOS lifts configData to environment.etc), with the absolute path injected back into configData.<name>.path so the service can refer to its files at a stable location:

{ config, ... }:
{
  home.services.demo = {
    process.argv = [ "/bin/myapp" "--config" config.home.services.demo.configData."app.toml".path ];
    configData."app.toml".text = ''
      port = 1234
    '';
  };
}

Scope notes

Home Manager mirrors the surface of nixpkgs' portable systemd module: services and sockets only. Other unit kinds Home Manager supports natively under (timers, paths, mounts, ...) are intentionally not modeled on home.services until upstream grows them, to keep both surfaces aligned.