Skip to main content
There are two supported ways to run russel-ctrl as a service: the installer’s systemd unit on Debian, Ubuntu, and similar distributions, or the NixOS module. Both listen on 127.0.0.1:7878 only, require the token, and keep state in /var/lib/russel with mode 0700.

What you’ll need

  • A russel-ctrl binary for this distribution: a release download, or one built on this machine. A binary built on NixOS won’t run on Debian.
  • sudo once, to install. Both setups run russel-ctrl and every workload as a dedicated, unprivileged russel account.
  • Rootless Podman for containers; /dev/kvm plus cloud-hypervisor, virtiofsd, and passt for microVMs.

Debian, Ubuntu, and other systemd distributions

check lists missing prerequisites with a fix for each and changes nothing. host runs the same checks first (--skip-checks overrides), then:
  • Creates the russel system group and account (home /var/lib/russel, no login shell), adds a subuid/subgid range after every existing one, and enables linger so user@<uid>.service gives it /run/user/<uid> and a user bus from boot.
  • Adds the invoking sudo user to the russel group, and adds russel to kvm when /dev/kvm exists.
  • Creates /etc/russel (root:russel, 0750) and /etc/russel/env (root:russel, 0640) when missing. Never overwrites, never prints tokens. An old per-user ~/.config/russel/env token is carried over.
  • Creates /var/lib/russel (russel:russel, 0700). A directory owned by anyone else is refused unless --take-state-ownership, which hands the whole tree to russel.
  • Renders contrib/russel-ctrl.service with russel’s uid into /etc/systemd/system/russel-ctrl.service (--force-unit overwrites a changed unit).
  • Installs /usr/local/bin/russel-ctrl, keeping the previous binary; a failed restart puts it back.
  • Enables or restarts the unit and probes /vms (bounded). Alive means HTTP 200 with a vms array, or HTTP 401 with WWW-Authenticate: Bearer realm="russel-ctrl".
It refuses NixOS, non-Linux, a russel.service from the NixOS module, a Nix-store unit, and an old per-user install (it prints the steps to move over). Manual equivalent (Debian/Ubuntu):
Unit defaults (see contrib/russel-ctrl.service): User=russel, loopback bind, RUSSEL_REQUIRE_AUTH=1, EnvironmentFile=/etc/russel/env, the Nix daemon profile on PATH, XDG_RUNTIME_DIR and the user bus for rootless Podman, Delegate=yes and KillMode=process so apps keep running when the control plane restarts, restart-on-failure.

Why the unit has no PrivateTmp or ProtectSystem

The dedicated, unprivileged russel account is what isolates Russel. The usual systemd sandboxing options break rootless Podman here:
  • Podman keeps a small pause process (catatonit -P) that holds the account’s namespaces, and every podman call joins it. It outlives the control plane on purpose, like the apps do.
  • It keeps the filesystem view of the control plane that first started it. With PrivateTmp=yes, that view’s /tmp and /var/tmp are deleted when the service restarts, and every later container fails with pasta … Failed to mount empty tmpfs for pivot_root() or mkdir /var/tmp/…: no such file or directory. ProtectSystem=strict would pin a read-only root the same way.
Don’t add these options back with systemctl edit. The control plane also runs Podman with --cgroup-manager=cgroupfs: from a system unit, systemd won’t let the account’s user manager take over container processes.

NixOS: services.russel

Module behavior:
  • Listens on 127.0.0.1:7878, requires the token, keeps /var/lib/russel at mode 0700, and turns the experimental warm pool off.
  • rootlessPodman (on by default) sets up what rootless Podman needs: it enables Podman, puts /run/wrappers on PATH so newuidmap is found, turns on linger, and turns off NoNewPrivileges, which blocks newuidmap. Turn it off only if you won’t run containers.
  • For microVMs, add services.russel.microvms.enable = true; and point services.russel.microvms.kernel at Russel’s kernel (the release’s russel-kernel-<version>-x86_64.bzImage, or the flake’s microvm-kernel package). For HTTPS, put Caddy, nginx, or Traefik in front (TLS reverse proxy).
  • The unit waits for network-online.target, and sets NIX_PATH to the nixpkgs the module was built with, so ctrl can build its helpers.
Create the token once, readable by root and the russel group, the same layout the installer uses. systemd reads it as root, so the group is only for people:
With your login user in the russel group, the laptop steps in Installation work unchanged, scp of /etc/russel/env included. The module doesn’t install the dashboard. With services.russel.bin = "/usr/local/bin/russel-ctrl", unpack the release’s russel-dashboard-<version>.tar.gz into /usr/local/share/russel/dashboard and ctrl finds it. Anywhere else, set services.russel.extraEnvironment.RUSSEL_DASHBOARD_DIR. Without either, ctrl serves the API only. Use services.russel, never install.sh host, on NixOS.

Verify