Skip to main content
Russel has two programs: This page gets both running. It takes about 15 minutes on a fresh server, most of it installing Nix and Podman.
On NixOS? Skip to NixOS. The rest of this page is for Debian, Ubuntu, and other systemd distributions.

How Russel runs

The installer creates a dedicated system account called russel. The control plane and every app run as that account, never as root and never as your own login user. So an app that escaped its container would find no SSH keys, no sudo, and none of your files. You need sudo once, to install. After that, nothing runs as root.

1. Prepare the server

You need a Linux x86_64 server with systemd. A cheap VPS without KVM is fine: apps run as rootless Podman containers by default. Install Podman and the pieces rootless Podman needs (Debian 12, Ubuntu 22.04 or newer):
Install Nix with the multi-user (daemon) installer, and turn on flakes for everyone:
Note: use the daemon install. The russel account builds through the Nix daemon; a single-user Nix install belongs to one user and can’t be shared with it.
Check the server. The installer can tell you what’s still missing, and how to fix it:
Fix anything marked FAIL. Lines marked warn are about optional features like microVMs. From a checkout, ./contrib/install.sh check does the same.

2. Install Russel on the server

Pick one of these. Both run the same checks first and stop if something is missing.

Option A: download a release

The installer downloads the binaries and checks each one against the release SHA256SUMS before installing anything. You must name a version; there is no “latest”. The release binaries need glibc 2.35 or newer (Ubuntu 22.04+, Debian 12+). On older distributions, on musl (Alpine), or on NixOS, build from source instead.

Option B: build from source

You need a Rust toolchain (rustup).
Build on the machine that will run the binary. A binary built on NixOS will not start on Debian.

What install.sh host did

  • Created the russel system account: no login shell, no sudo, home /var/lib/russel. It gets its own range of sub-IDs for rootless Podman, and “linger”, so it runs from boot without anyone logged in.
  • Created /var/lib/russel (mode 0700, owned by russel). All Russel state lives here.
  • Generated an API token in /etc/russel/env (owner root, group russel, mode 0640). The token is never printed.
  • Added you (the user who ran sudo) to the russel group, so you can read the token.
  • Installed russel-ctrl to /usr/local/bin and the dashboard to /usr/local/share/russel/dashboard.
  • Installed and started the system service russel-ctrl, running as russel and listening on 127.0.0.1:7878 only, with authentication required.
install.sh cli installed russel to ~/.local/bin. Make sure that directory is on your PATH.
Log out and back in (or run newgrp russel) before the next step. Group membership only applies to new logins.

3. Connect the CLI

The control plane only listens on 127.0.0.1. Never expose port 7878 to the network. Reach it in one of three ways.

Same server

russel origin shows which control plane the CLI talks to and whether it is reachable. russel ps shows an empty table on a fresh install. Install the CLI on the laptop (Option A with cli, or Option B’s install.sh cli). Then copy the token and open a tunnel:
From a checkout, ./contrib/install.sh connect user@server does the same. connect starts a background SSH forward from the laptop’s 127.0.0.1:7878 to the server’s. It fails loudly if the forward cannot be set up. Keep it running while you use the CLI or dashboard. If you prefer to run it by hand:

From anywhere, over HTTPS

Put Caddy or nginx in front of 127.0.0.1:7878 on the server, then log in with the HTTPS URL:
The CLI refuses to send the token over plain http:// to anything but loopback. Proxy configs: TLS reverse proxy.
Fish shell: don’t source the env file; fish can’t read KEY=VALUE files. --token-file works in every shell.

4. Open the dashboard

Open http://127.0.0.1:7878/ (through the tunnel from a laptop, or your HTTPS URL). Paste the token from /etc/russel/env into Settings. The dashboard keeps it for that browser tab only; russel login does not fill it in. More: Dashboard.

5. Check the install

On the server:
The control plane’s log is /var/lib/russel/ctrl.log; reading it needs sudo, because only russel can open that directory. journalctl -u russel-ctrl shows start-up errors. You’re ready: Quickstart deploys an example app in a few minutes.

Optional: microVMs (experimental)

Containers are the default and what most servers should use. MicroVMs give each app its own kernel through Cloud Hypervisor. They need hardware virtualization, which most cheap VPSes don’t expose. You need cloud-hypervisor v52 or newer (older versions hang when cpus > 1), virtiofsd, and passt on PATH. install.sh check warns about each missing one. When /dev/kvm exists, install.sh host adds russel to the kvm group and installs the release microVM kernel at /var/lib/russel/_pool/kernel/bzImage. Nothing else needs root. Without /dev/kvm, the installer prints “containers only” and moves on. To use your own kernel, build nix build .#microvm-kernel and set RUSSEL_KERNEL_PATH to result/bzImage in /etc/russel/env.

NixOS

Use the services.russel module, not install.sh host and not the release binaries. Build russel-ctrl from source on the NixOS machine (Option B, cargo build only), install it, and point the module at it:
It works the same way as the installer: a dedicated russel account, 127.0.0.1:7878, authentication required, /var/lib/russel at mode 0700, rootless Podman. The service is russel.service (systemctl status russel). For microVMs add services.russel.microvms.enable = true;. Details: systemd + NixOS.

Upgrade

Run the same install command with the new version. Your token and /var/lib/russel are kept, and the service restarts:
From source: git pull, rebuild, and run sudo ./contrib/install.sh host again. If the new binary fails to start, the installer puts the old one back. Never delete /etc/russel/env or /var/lib/russel when upgrading. Backups: Upgrades + backups.

Moving from an older per-user install

Earlier builds of install.sh host ran russel-ctrl as your own user, from ~/.config/systemd/user. The new installer finds that setup and stops with the steps to move over:
  1. russel ps, then russel destroy each service. The new russel account can’t take over containers that your own Podman started.
  2. systemctl --user disable --now russel-ctrl, then delete ~/.config/systemd/user/russel-ctrl.service.
  3. sudo ./contrib/install.sh --take-state-ownership host. This gives /var/lib/russel (secrets, history, volumes) to russel, and keeps the token from ~/.config/russel/env, so your existing russel login keeps working.
Then russel deploy your services again.

Uninstall

Remove the control plane with:
The command stops and disables russel-ctrl, turns off linger for russel, ends its remaining processes, and deletes the unit, /usr/local/bin/russel-ctrl (and the .previous backup next to it), and /usr/local/share/russel. It keeps /var/lib/russel and /etc/russel/env, so a later install.sh host picks up your services, volumes, and token. It also keeps the russel account and its subuid/subgid ranges. Containers live in the russel account’s Podman store, so the command stops when services still exist. Run russel ps, then russel destroy each service, then uninstall. sudo ./contrib/install.sh uninstall --force skips that check and leaves the containers orphaned. To remove everything, add --purge:
This also deletes /var/lib/russel (secrets, history, volumes), /etc/russel, the russel user and group, and its lines in /etc/subuid and /etc/subgid. Back up anything you need first: Upgrades + backups. The flags also work before the subcommand, so --force --purge uninstall does the same thing. The CLI on your laptop is separate: delete ~/.local/bin/russel if you want it gone.

Installer reference