service.type in the Russelfile picks how an app is isolated:
Neither runtime needs root.
What the app sees
Both runtimes give the app the same filesystem, so a Russelfile behaves the same on either:
An app that writes anywhere else gets
Read-only file system.
The app runs as an unprivileged user that owns its volumes. It can still bind ports below 1024, and HOME is /tmp unless you set it. For an app that must run as root inside its sandbox, set service.user = "root".
Russel sets PORT to service.port. Listen on 0.0.0.0: an app bound to 127.0.0.1 can’t be reached from outside its container.
Containers
- No image. Russel builds a tiny root folder (a few
/etcfiles and a link to your binary) and starts it withpodman run --rootfs, with the host’s/nix/storemounted read-only. There is no registry, no pull, and no Dockerfile. - Locked down by default. All Linux capabilities are dropped, privilege escalation is blocked, the root is read-only, and memory is capped at
service.memory.debug = trueadds a shell and curl for troubleshooting; don’t leave it on. - Entry point. The binary must be statically linked or use an absolute
/nix/storeinterpreter, because there is no shell or/usr/bin/env. - Ready means answering. A deploy waits up to 30 s for the app to accept a connection on its published port and keep it open. A container that exits or restarts during that time fails the deploy at once, with its exit code and last 40 lines of output.
- Watched after deploy. Russel checks the container every second for the first minute, then every 5 s. If it exits, is removed, or is restarted by Podman, the service shows
failed. See Lifecycle: Health. - Logs are written to
/var/lib/russel/<id>/container.log.
podman inspect as the russel account. secret:// values are passed as Podman secrets and don’t show up there, so put anything sensitive behind secret://.
MicroVMs (experimental)
Each app gets its own Linux kernel. The VM boots in about 2 s into a tiny init that mounts the host’s/nix/store and starts the app.
- Needs: read-write
/dev/kvm(the installer addsrusselto thekvmgroup),cloud-hypervisorv52 or newer (older versions hang withcpus> 1),virtiofsd, andpasstonPATH. A deploy without them fails before the build and names what’s missing. - Networking uses
passt, which gives the VM a network card and publishes its port on the host, the same jobpastadoes for rootless Podman. - Files reach the VM over virtiofs:
/nix/storeread-only, the deploy config read-only, and each volume as its own share. The app is never copied. - Kernel. Use Russel’s kernel, which has the virtio drivers built in. The installer puts it at
/var/lib/russel/_pool/kernel/bzImage. - When the app exits, the init powers the VM off, and Russel sees the VM stop. A deploy fails at once with the app’s exit code and the end of its console output, in
/var/lib/russel/<id>/console.log. - Resources:
service.memoryof RAM andservice.cpusvCPUs (default 1).
/config and /run/russel, so volumes can’t use those paths.
A control plane running as root uses a TAP device, socat, and an iptables chain in place of passt. That setup exists for development and benchmarks; see Networking.
Guest userspace
service.guest is separate from service.type: it picks what runs inside the sandbox.
Choosing
- No
/dev/kvm, which is most cheap VPSes: use containers. - KVM hardware and you want to try a separate kernel per app:
type = "microvm", knowing it’s experimental.