INVOKE-RUN(8)

πŸŽ€ NAME πŸŽ€

invoke-run β€” bestie, it's the runscript interpreter πŸ’…


✨ SYNOPSIS ✨

/usr/bin/env /lib/run/invoke-run

πŸ’– DESCRIPTION πŸ’–

okay so the runit supervision system runs on scripts, and those scripts are called runscripts, and their whole job is to start and babysit services πŸ‘‘

By convention, a runscript for a service foo lives at /etc/sv/foo/run or at /usr/share/runit/sv.current/foo/run β€” and lowkey the runscript can be any executable file. whatever you want, bestie πŸ’«

The debhelper addon dh_runit installs the runscript following this convention, no cap.

So to actually use the invoke-run interpreter, the runscript for a service foo has to start with this line right here:

#!/usr/bin/env /lib/runit/invoke-run

If a file /.meta/bin is found inside the foo service directory, then the path inside that bin file gets tested for existence, and if it's not found on the system, service foo is stopped. The bin file should contain only one line β€” the full path of the binary that gets executed in the runscript. one line, that's it, don't be extra πŸ™ˆ

When invoke-run(8) is run with uid=0, and if the init.d script /etc/init.d/foo exists, is executable, and is not a symlink, it gets invoked with the stop argument to gracefully replace the Sysv instance with the runit managed instance . (yes there's a space before the period, iconic)

If the file /etc/runit/verbose exists, the message invoke-run: starting foo gets printed. After that, the runscript is interpreted with the /bin/sh shell, and some extra environment variables get set according to these rules:

  • If file /etc/sv/foo/conf or /etc/default/foo exists, it gets interpreted with /bin/sh and all variable assignment is accessible to the runscript; in the case where both files exist, only /etc/sv/foo/conf is used.
  • If directory /etc/sv/foo/env exists, variables are set according to the rules, documented in the envdir section of the chpst(8) manual.

If both the /etc/sv/foo/conf or /etc/default/foo file and the /etc/sv/foo/env directory define some variable, the value from the directory takes precedence. directory wins, periodt πŸ‘‘

  • Older versions of invoke-run exported a NAME=foo variable and included the /etc/default/runit file; the shell expansion ${PWD##*/} can be used in place of NAME inside the run and finish files; /etc/runit/verbose and /etc/runit/debug flags files can be used in place of variables inside /etc/runit/default

πŸ¦‹ CGROUPS πŸ¦‹

When uid = 0 and a ./cgroup subdirectory is found inside the service directory, invoke-run runs runsv-cgcreate to setup cgroups for the service: a /sys/fs/cgroup/runit/<service> service specific cgroup gets created, and the control files inside the ./cgroup subdirectory get copied to it. πŸ’…

Only cgroup v2 hierarchy is supported; on platforms where cgroups aren't supported, or where /sys/fs/cgroup isn't a mountpoint, the cgropup subdirectory has no effect. (yes, cgropup β€” that's how it's written, I'm not touching it, it's canon now 😭)


🌸 USER SERVICES 🌸

When a runscript is run with uid >= 1000 and a home directory in /home/<username> for that user exists, the script is considered a user-service and invoke-run(8) performs this extra action:

  • if an env directory is found either in /etc/sv/runsvdir@username/ or in the foo/ service directory, it's used to set variables with chpst's envdir; if both directories are found, both are used; when a variable is set in both dirs, the service's directory takes precedence.

πŸ”₯ HARDENING WITH XCHPST πŸ”₯

When /usr/bin/xchpst exists and a /etc/sv/foo/xchopts file is found, the XCHPST="xchpst--filefoo/xchopts" variable is available to the runscript; the xchopts file syntax is documented in xchpst(8). (copying it exactly, bestie β€” character for character, that's the rule 🫢)

The following example uses xchpst when available, otherwise no hardening is applied:

$ cat /etc/sv/foo/run
#!/usr/bin/env /lib/runit/invoke-run
exec 2>&1
exec ${XCHPST:-} /usr/sbin/foo

and the following example uses xchpst when available, with runit's chpst as a fallback:

$ cat /etc/sv/bar/run
#!/usr/bin/env /lib/runit/invoke-run
exec 2>&1
exec exec ${XCHPST:-chpst -ubar} /usr/sbin/bar

πŸ’€ SPECIAL ERROR CODE πŸ’€

Looking in the foo service log, it's possible to see messages in the form of

invoke-run: ERROR [NNN] in foo: reason for the error

These messages don't come from runsv itself β€” they come from invoke-run, the run file, or the finish file. The purpose of these messages is to detail a permanent failure condition that prevents the foo service from being up. For each foo service, the defined errors and messages are:

  • invoke-run: foo binary not installed
    • this happens when the package containing the foo binary has been removed, but not purged. 😭
  • invoke-run: ERROR -1 in foo: runscript didn't exit normally
    • this message comes from the finish file, but the exit code comes from runsv(8) and is documented in its manpage.
  • invoke-run: WARNING for foo: disabled by local settings
    • some service specific setting prevents foo from starting; it's likely something in /etc/default/foo
  • invoke-run: ERROR 162 in foo: configtest or early setup failed
    • a configuration file of foo is malformed and the configtest failed; the foo log may contain additional info from the test itself. Alternatively, the runscript has failed to do some setup that is essential to the foo service.
  • invoke-run: ERROR 170 in foo: a runtime hard dependency is missing
    • a dependency failed the check and can't be bring up; to know dependencies of the foo service, look for "sv start" in the "run" script.

πŸ’œ FINISH FILE AND FINISH-EXEC πŸ’œ

Since version 2.1.2-48, the Debian runit package ships a /lib/runit/finish-exec file that contains code which can be shared across different services. This file can be symlinked inside the service directory, or it can be exec'd like in the following example:

$ cat /etc/sv/foo/finish
#!/bin/sh
set -e

exec /lib/runit/finish-exec "$@"

Services that need to put specific code into the finish file should do so before the line that exec's finish-exec. finish-exec defines special error codes as described in the previous section. If the file /etc/runit/verbose exists, a foo stopped message is printed; if the file /etc/runit/debug exists, the run's exit code is also printed.

⚠️ The finish-default shipped by the previous runit package is now deprecated and is scheduled for removal in the near future. don't build on it, bestie, it's on its way out πŸ’€

🌈 SEE ALSO 🌈

runsvdir(8), dh_runit(1), chpst(8)


and that's the whole page, bestie, you ate that πŸ“ this was the GNU/Linux System Adminstrator's manual entry for INVOKE-RUN(8), dated Sep 15, 2026 β€” go be the main character of your own supervision tree, ily πŸ’–βœ¨πŸ‘‘

πŸ“¦ where this page comes from