A friendly wrapper around runit's sv command, with system and user service support.
Why?
I don't hate myself and this is a nice wrapper.
The inspiration was OpenRC's rc-service and rc-update.
I hate doing ln -s /etc/runit/sv/service_name /run/runit/service — that gets annoying fast, and I don't have to think about it with this.
Other runit service managers
Before starting this project I wasn't aware of existing tools. Here's how they compare:
| Tool | Language | Last Updated | Notes |
|---|---|---|---|
| rsv | Rust | ~3 years ago | Abandoned, list is raw sv status dump |
| vsv | Rust | ~3 years ago | Rewrite of bash-vsv, Void-focused, Abandoned |
| bash-vsv | Bash | ~3 years ago | Original vsv, Void-focused, Abandoned |
| rsm | Bash | ~5 years ago | Attributed fork of bash-vsv for Artix, adds logs/errorlogs, shows internal runit dirs as services, Abandoned |
| roonit | C | ~5 years ago | Void-focused, very basic, Abandoned |
| Tool | Time | Output |
|---|---|---|
rsv (JojiiOfficial) |
~14ms | Raw unsorted sv status dump, no colors, includes internal dirs |
rsm |
~168ms | Formatted, but includes current/supervise as services |
rsv (this) |
~32ms | Sorted, colored, filters internals, enabled/disabled/uptime distinction |
bash-vsv |
~168ms | Estimated, shares the same core codebase as rsm |
| others | ? | Untested, don't use Void |
rsm is a properly attributed fork of bash-vsv, retargeted at Artix's paths with a couple of added subcommands (logs/errorlogs). The core service-listing/status logic is unchanged from bash-vsv, which is why the two benchmark identically.
If you use Void and want to benchmark, I will add those.
A prebuilt rsv-ng package is published via CI from this repo:
xbps-install -R https://rsv-main-f9fead.gitlab.io/ARCH rsv-ngReplace ARCH with x86_64 or x86_64-musl, matching xbps-uhelper arch on your system. Browse the repo (and get the current URL, since GitLab's unique-domain hash can change if Pages is reconfigured) at https://rsv-main-f9fead.gitlab.io/.
yay -S rsv-ng
paru -S rsv-ngRun the install script as root (system-wide) or as your user (local):
sudo ./Install.sh # installs to /usr/local/bin, system completion dirs
./Install.sh # installs to ~/.local/bin, user completion dirsManual installation
install -Dm755 rsv /usr/local/bin/rsv
install -Dm644 rsv.bash /usr/share/bash-completion/completions/rsv
install -Dm644 rsv.fish /usr/share/fish/vendor_completions.d/rsv.fish
install -Dm644 rsv.zsh /usr/share/zsh/site-functions/_rsv
install -Dm644 rsv.1 /usr/share/man/man1/rsv.1install -m755 rsv ~/.local/bin/rsv
install -m644 rsv.bash ~/.local/share/bash-completion/completions/rsv
install -m644 rsv.fish ~/.config/fish/completions/rsv.fish
install -m644 rsv.zsh ~/.local/share/zsh/site-functions/_rsvNote: For sudo rsv and man pages to work, system-wide installation is recommended.
rsv [--user] [--as-user <user>] <command> [service] [flags]
System services require root. User services are selected automatically when not root, or explicitly with --user.
| Command | Description |
|---|---|
start <service...> |
start one or more services |
stop <service...> |
stop one or more services |
restart <service...> |
restart one or more services |
reload <service...> |
reload one or more services |
enable <service...> |
enable one or more services |
disable <service...> |
disable one or more services |
status [service...] |
status of listed (or all enabled) services |
list |
list all available services and their state |
logs [service] |
tail logs for a service, or the global log |
edit <service...> |
open the run script(s) in $EDITOR |
new <service> |
scaffold a new service |
init |
start the user runsvdir supervisor (user mode only) |
doctor |
check for common runit configuration problems |
once <service...> |
run one or more services once without supervision |
watch [service] |
auto-refreshing status display |
log-setup <service...> |
add a svlogd log service to one or more services |
log-remove <service...> |
remove the svlogd log service from one or more services |
finish-setup <service...> |
scaffold a finish script for one or more services |
| Flag | Applies to | Description |
|---|---|---|
--user |
all | operate on user services (auto-set when not root) |
--as-user <user> |
all | manage another user's services (requires r/w access) |
--now |
enable |
also start the service immediately |
--log |
new |
also scaffold a log service |
-u, --uptime |
list |
show uptime for running services |
--interval <N> |
watch |
refresh every N seconds (default 2) |
--errors |
logs |
show only error/warn/crit/fail lines |
--level <levels> |
logs |
filter by log level, e.g. --level error,warn |
--lines <N> |
logs |
show last N matching lines (default 10) |
# system services
sudo rsv status
sudo rsv restart NetworkManager
sudo rsv enable --now sshd
sudo rsv list
sudo rsv list -u
sudo rsv logs NetworkManager
sudo rsv logs NetworkManager --errors
sudo rsv logs NetworkManager --level info,warn
# watch services refresh every 5 seconds
sudo rsv watch --interval 5
# user services
rsv status
rsv enable --now pipewire
rsv logs syncthing
# create a new service with logging
sudo rsv new myapp --log
sudo rsv edit myapp
sudo rsv enable --now myapp
# manage another user's services (requires r/w access to their runit dirs)
sudo rsv --as-user bob enable pipewire
sudo rsv --as-user bob logs pipewireIf you run a command against the wrong mode, rsv will reexec as root via sudo or doas
$ rsv enable metalog
[sudo] password for cytop:
ok: enabled 'metalog'
User services live in ~/.runit/sv with symlinks in ~/.runit/runsvdir. Start the user supervisor at login by adding this to your session init (~/.profile, ~/.xinitrc, etc.):
rsv --user initrsv escalates privileges only through the user's existing sudo or doas configuration. When a command needs root and wasn't invoked with one already, rsv re-execs itself via whichever is installed (sudo preferred, doas as fallback) and returns an error if neither is available. It never bypasses or replaces the system's authentication policy.
Note:
doasrequires an explicit rule in/etc/doas.conf(e.g.permit :wheel cmd /usr/bin/rsv) for this to work — without one,doaswill refuse silently rather than rsv being at fault.
--as-user <user> is a separate mechanism and does not escalate privileges. It only checks that the invoking (already-privileged) user has read/write access to the target user's runit directories — or their nearest existing parent directory, if the target doesn't exist yet — and operates entirely within those existing filesystem permissions.
Paths can be overridden with environment variables:
| Variable | Default |
|---|---|
RUNIT_SVDIR |
~/.runit/sv |
RUNIT_RUNSVDIR |
~/.runit/runsvdir |
RUNIT_LOG |
~/.runit/log/everything/current |
| System | User | |
|---|---|---|
| service definitions | /etc/runit/sv |
~/.runit/sv |
| enabled services | /etc/runit/runsvdir/default |
~/.runit/runsvdir |
| global log | /var/log/everything/current |
~/.runit/log/everything/current |
| per-service log | /var/log/<service>/current |
~/.runit/log/<service>/current |
| System | User | |
|---|---|---|
| service definitions | /etc/sv |
~/.runit/sv |
| enabled services | /var/service |
~/.runit/runsvdir |
| global log | /var/log/sv/everything/current |
~/.runit/log/everything/current |
| per-service log | /var/log/sv/<service>/current |
~/.runit/log/<service>/current |
| System | User | |
|---|---|---|
| service definitions | /etc/sv |
~/.runit/sv |
| enabled services | /etc/runit/runsvdir/default |
~/.runit/runsvdir |
| global log | /var/log/runit/everything/current |
~/.runit/log/everything/current |
| per-service log | /var/log/<service>/current |
~/.runit/log/<service>/current |
rsv detects whether runit is actually running by checking if PID 1's process name is runit or runsvdir, falling back to a pgrep scan for either process — this correctly handles containers where runit runs as PID 2+ under an init shim like tini or docker-init.
On Void (and any distro whose default runsvdir is /var/service), that same check also covers chroots: /var/service only reflects reality once runit is actually supervising it, so whenever runit isn't running, rsv operates on /etc/runit/runsvdir/default instead — matching Void's own documented chroot install steps (and how Void's installer itself enables services pre-boot).
$ rsv list
==> Available user services (in /home/cytop/.runit/sv):
warn: service directory /home/cytop/.runit/sv does not exist
warn: runit is not running
___________________________________
< rsv is for runit, not for systemd >
-----------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Note: Each taunt (systemd, OpenRC, dinit, s6, 66, and sysvinit) is pre-rendered and base64-embedded, so there's no runtime
cowsaydependency. I got the idea due topacman'srepo-elephant.
Services that write to stdout/stderr can have a dedicated svlogd log service added with log-setup:
sudo rsv log-setup myappThis creates a log/run script that pipes the service's output through svlogd into its own log directory. Once active, rsv logs myapp reads from there directly instead of filtering the global log.
Note: Some services (e.g. NetworkManager) log via syslog rather than stdout, so their per-service log will be empty. The global log is the right source for those.
To remove per-service logging:
sudo rsv log-remove myappLog data is left in place — remove it manually if no longer needed.
Completions for fish, bash, and zsh are included. The install script places them automatically.
Commands, flags, and service names are all completed. Flags only appear for the commands they apply to — --now for enable, --log and --user for new, -u/--uptime for list, --interval for watch, and --errors/--level/--lines for logs. log-setup and finish-setup only suggest services that don't already have a log service or finish script respectively.
rsv respects the NO_COLOR convention. When running under sudo, it recovers the variable from the parent shell's environment since sudo strips it. TERM=dumb also disables color.