S6-RC(8)

πŸ“¦ version 0.7.0.0, in nixpkgs unstable

✨ NAME ✨

Okay so welcome to s6-rc(8), bestie β€” straight out of the System Manager's Manual β€” and this page is literally about managing the live state of a machine: what services are currently up and what services are currently down. That's the whole vibe. πŸ’–

The command is s6-rc, and it's giving main character energy for your service state. No cap. 🫢


🌸 SYNOPSIS 🌸

Here's the exact choose-your-own-adventure lineup, character for character:

s6-rc help
s6-rc [-l live] [-b] [-a] [-u | -d] [-e | -E] list servicenames...
s6-rc [-l live] [-b] [-a] [-u | -d] [-e | -E] listall servicenames...
s6-rc [-l live] [-b] diff
s6-rc [-l live] [-b] [-a] [-u | -d | -D] [-p] [-v verbosity]
      [-n dryrunthrottle] [-t timeout] change servicenames...

✨ DESCRIPTION ✨

okay so s6-rc is a service manager, a.k.a a machine state manager β€” it's literally the program that manages the live state of your machine, meaning: which services are currently up and which ones are currently down. It can list out your active services, or it can actually change the live state. iconic behaviour tbh πŸŽ€

s6-rc is meant to be the one-stop shop of service management, no cap. Once your service definitions have been turned into a compiled service database via s6-rc-compile(8), and the machine state has been initialized via s6-rc-init(8), then any state change β€” whether that's initial startup, shutdown, or literally anything else β€” should be achieved by a single s6-rc change invocation. one call, done, periodt πŸ’…

s6-rc should only be run by the user owning the underlying s6 supervision tree. It can also be run by root even if the supervision tree isn't owned by root, but heads up bestie: in that case the services will always be started and stopped by the user owning the supervision tree β€” they will not have root privileges. s6-rc expects to find a functional live state in live. The live state is normally created at boot time by s6-rc-init(8). If the live state is invalid, s6-rc complains and exits 4. πŸ’€

Depending on the arguments you give it, it either prints the requested information to stdout and then exits 0, or it performs a machine state change. that's the whole vibe πŸ¦‹

🌸 Service selection 🌸

The arguments servicenames... may be atomic services or bundles β€” they get resolved into a set of atomic services, and that set is called the selection. If the -a option is present, the current set of active services (i.e. the services that are up) gets added to the selection. The s6-rc command always operates on the selection. always always, no exceptions 🫢

πŸ¦‹ Longrun transitions πŸ¦‹

Transitions for longrun services are simple, ngl: s6-rc removes or creates a ./down file in the live service directory for the longrun service, then sends a command to the supervision tree to start or stop the service. (A service that's considered down by s6-rc will have a ./down file in its live service directory; a service that's considered up by s6-rc will not.) Simple as πŸ“

The transition is considered successful as soon as the daemon dies (for down transitions), or becomes up and ready (for up transitions) β€” refer to s6-notifywhenup(7). If a longrun service doesn't support readiness notification, the s6-svc(8) command that's invoked by s6-rc will print a warning message, and the transition will be considered successful as soon as the daemon is up, i.e. as soon as the ./run script is executed by s6-supervise(8).

When a longrun service does support readiness notification, unless a nonzero timeout has been declared in the timeout-up file in the service definition directory, s6-rc will wait forever on an up transition for the notification to arrive 😭. The transition will fail if a timeout occurs.

If a down transition fails, s6-rc does nothing with it. The service has already received a SIGTERM, and may be stuck in the process of exiting; or it may already have died but is stuck in a bad finish script that isn't timing out. In any case, it's not a situation that s6-rc can recover from β€” the service is most likely down, but the administrator should manually check their process list. And fix their scripts, or timeout values, because a down transition failure is always a programmer or sysadmin error. no cap, it's giving user error πŸ’€

If an up transition fails, s6-rc sends an explicit s6-svc -d command to the longrun. This ensures the service is in a known down state when failing to go up, instead of (for instance) being stuck in a not-ready limbo state. we love a clean exit, we hate a limbo vibe πŸ™ˆ

Note that proper usage of the timeout-kill and timeout-finish values in the longrun's definition directory can considerably reduce the number of cases where the service is left in an unknown state. highly recommend, bestie πŸ’–

Transitions are supposed to be idempotent, but it's a general rule of supervision that run and finish scripts must be idempotent anyway, so a properly designed service directory should work with s6-rc with no additional effort. slay πŸ”₯

πŸ’œ Oneshot transitions πŸ’œ

Transitions for oneshot services amount to running the up or down script for the service; those scripts are stored in the compiled service database that's linked from the live state. The transition is considered successful if the script exits zero, and unsuccessful otherwise. that's it, that's the whole rule πŸŽ€

s6-rc performs some black magic so that up and down scripts are always run in a reproducible way, no matter when or how s6-rc change is invoked. That black magic involves a special longrun service, s6rc-oneshot-runner, that every oneshot service automatically depends on, and that's actually used to fork the up or down scripts as scions of the s6 supervision tree, instead of children of the s6-rc process. genuinely so cool, iykyk 🧚

Transitions should ideally be transactional, or at the very least idempotent. If a transition fails, it should leave the machine in the same state as before the transition was attempted; at the very least, it should not prevent a subsequent run of the same transition from completing successfully. If an s6-rc change invocation fails because some transition experienced a temporary failure, it should be possible to run the exact same s6-rc change invocation later, and be met with success. yes bestie, retry is real πŸ’…

This is important: it means that oneshot scripts should be treated as atoms, and that some care should be taken when writing them. fr fr, write them with intention 🌸

πŸŽ€ Dry runs πŸŽ€

For any manual change, it's recommended to perform a dry run before the state change itself: add the -n dryrunthrottle option to the s6-rc command line. s6-rc will then simulate all the transitions, but not actually perform them or change the real live state. The command lines that s6-rc would have run will be printed to stdout instead, and each simulated transition will take dryrunthrottle milliseconds to complete successfully. look before you leap, bestie πŸ‘‘

⭐ Subcommands ⭐

  • s6-rc help β€” Prints a short help message. short and sweet 🫢
  • s6-rc list servicenames... β€” Prints the selection. If the -d option has been given, the selection is inverted before it's printed, i.e. all the services but servicenames... will be printed.

This is mostly useful as s6-rc -a list, which simply prints the list of currently active services. s6-rc -da list will print the list of currently down services. so handy 🌈

If the -e option has been given, the services marked as essential (i.e. which have the flag-essential file in their source definition directories) will not be printed.

  • s6-rc listall servicenames... β€” s6-rc computes the complete dependency tree of the selection: the selection, plus the dependencies of every service in the selection, and recursively down. With the -d option, it computes the reverse dependency tree: the selection, plus what depends on it, and recursively up. The result is called the closed selection.

It prints the closed selection to its standard output. Note that the closed selection may be a very long list of services, including the most basic ones. like, don't say we didn't warn you 😭

If the -e option has been given, the services marked as essential (i.e. which have the flag-essential file in their source definition directories) will not be printed.

  • s6-rc diff β€” Checks the consistency between s6-rc's view of the state of the longruns it's managing, and s6's view. s6-rc will print one line per service whose s6 state fails to match what s6-rc believes: a + character (if s6 is maintaining the service as up when s6-rc thinks it's down) or a - character (if s6 is not trying to maintain the service when s6-rc thinks it's up), followed by the service name. If s6-rc finds at least one mismatch, it exits 1; else it exits 0. s6-rc diff normally reports nothing. Currently, the only case where s6-rc's view can diverge from s6's view is when a service has been brought up via an s6-rc change command, then has failed at some point with a permanent failure β€” which means its supervisor has stopped trying to bring it back up. vibe check: failed βœ… πŸ’€
  • s6-rc start β€” s6-rc start is equivalent to s6-rc -u change, see below. It will run the engine to start a set of services. go go go πŸ”₯
  • s6-rc stop β€” s6-rc stop is equivalent to s6-rc -d change, see below. It will run the engine to stop a set of services. bye bestie πŸ‘‹
  • s6-rc change β€” s6-rc change is the service state engine. It will bring the machine to a state where:
    • -u β€” (Default) All the services in the selection are up. ✨
    • -d β€” All the services in the selection are down.
    • -pu β€” All the services in the selection, and only them, are up.
    • -pd β€” All the services in the selection, and only them, are down.

(Note that the -p option is inoperant when using the start or stop subcommands.) just so you know 🫢

To change the machine state, s6-rc does the following:

  1. From the selection, s6-rc computes the closed selection.
  2. It then computes the necessary transitions so that all the services in the closed selection can match the desired state.
  3. It then performs the transitions β€” or simulates them if the -n option has been given.
  4. s6-rc processes services as soon as they can be processed: as soon as a service that needs to change state has all its dependencies fulfilled, it undergoes its transition. Independent services are processed in parallel β€” s6-rc parallelizes the transitions as much as it can, and there is no useless waiting time. efficiency queen πŸ‘‘
  5. s6-rc updates the live state in live after every successful transition.
  6. If every transition completes successfully, the live state eventually matches the desired state, and s6-rc exits 0. we love a happy ending πŸ’–
  7. If a transition fails, s6-rc will not perform the transitions that depend on it. It will keep processing all the other independent transitions, though, until there's no more work it can complete; then it will exit 1. graceful degradation, we stan 😭

s6-rc change reacts to the following signals:

πŸ’… SIGTERM

s6-rc immediately aborts all its longrun transitions with a failure, and the impacted longruns will most likely be in a down state. Oneshot transitions are untouched, because killing the oneshot subprocess would make it impossible to determine what state the oneshot service is in. don't touch the oneshots, they're doing their best πŸ™ˆ

πŸ’… SIGINT

Same as SIGTERM. twins fr fr ⭐

✨ OPTIONS ✨

πŸ’– General options πŸ’–

Ok bestie, these are the general-vibe knobs. No drama.

  • -l live β€” Hunt for the live state inside live. Default is /run/s6-rc β€” iconic default, honestly. You can change that default at compile-time by passing the --livedir=live option to ./configure.
  • -a β€” Add the current set of active services to the selection. This is lowkey useful at shutdown time: s6-rc -da change will stop all services. Slay.
  • -b β€” Blocking lock. If the service database is currently being used by another program, s6-rc will wait until that other program has released its lock on the database, then proceed. By default, s6-rc fails with an error message if the service database is currently in use. The default is the safe behaviour: for instance, it will correctly detect and fail nested s6-rc invocations (which are an admin error), whereas s6-rc -b would deadlock in such a case.
Bestie, that -b deadlock note is not a fake-out. Nested invocations plus blocking lock = stuck city. The safe default is giving what it's supposed to give. πŸ’…

πŸ¦‹ Up or down πŸ¦‹

These options control what is to be done: bring selected services up or down (for s6-rc change) or whether to use the forward or reverse dependency graph (for s6-rc listall). Default is up.

  • -u β€” Up. The default, the main character, the "we're going up" option.
  • -d β€” Down.
  • -D β€” Force down.

The -d option is what should normally be used to down a set of services. If a service is marked as essential (there is a flag-essential file in its service definition directory), then s6-rc will refuse to stop that service. The -D option will still force that service to shut down; that option should only be used when the machine is going to be stopped.

So like… -D is the "we are literally shutting the whole machine off" button. Do not casually use it to be cute. Essential services said "not today", and -d respects that. 🫢

πŸŽ€ β€˜s6-rc change’ control πŸŽ€

These are the controls for the change girlies. This is where the transitions get their little settings.

  • -v verbosity β€” Be more or less verbose. Default is 1: warning and error messages will be printed to stderr. 0 silences warnings. 2 writes information messages whenever s6-rc performs a transition. 3 or more is debug info.
  • -n dryruntimeout β€” Dry run. s6-rc will pretend to perform transitions, but will replace all its program invocations by a call to s6-rc-dryrun(8), which will do nothing but print the command line s6-rc would have executed, then sleep for dryruntimeout milliseconds before reporting success.
  • -t timeout β€” Timeout. If s6-rc isn't done after timeout milliseconds, it will exit, leaving the live state as it is at exit time. It does not kill its children, so a child may successfully complete afterwards and the live state will not be updated; in that case, subsequent s6-rc change invocations will notice and correctly update it.
  • -p β€” Prune. The state will be brought to exactly the set of selected services, plus their dependencies, and the other services will be brought down. With the -d option, the meaning is reversed: the state will be brought to the maximum possible set that does not include selected services. The change, however, is always performed in the same manner: first the unwanted services are all brought down, then the wanted services are all brought up.

🌈 EXIT STATUS 🌈

  • 0 β€” Success. We ate.
  • 1 β€” Some of the state transitions could not be performed by s6-rc change; or a mismatch was found by s6-rc diff.
  • 2 β€” Timed out during state change.
  • 3 β€” Unknown service name in the arguments.
  • 4 β€” Invalid service database.
  • 100 β€” Wrong usage.
  • 111 β€” System call failed.

πŸ“ EXAMPLES πŸ“

s6-rc start myservicebundle

Brings up all the services represented by myservicebundle, bringing up all its dependencies first (recursively). Very "we're all in this together" of it.

s6-rc -ba down

Waits for any pending program of the s6-rc family to stop using the live database and current compiled service database, then brings down all the currently running services in an orderly manner. This is typically run at shutdown time. (And it's not necessarily a bad change!)

s6-rc -l /zork -ua listall myservicebundle

Prints the names of all atomic services represented by myservicebundle plus the current live services, as well as everything they depend on, recursively. Assumes the live state is stored in the /zork directory instead of /run/s6-rc. Custom directory moment. πŸ¦‹

s6-rc -d listall myservicebundle

Prints the names of all atomic services represented by myservicebundle, as well as everything that depends on them.

s6-rc -pun0 change myservicebundle

Prints what s6-rc would do to bring the state to exactly the contents of myservicebundle as well as its recursive dependencies, and pruning all the rest. Does not wait any extra time between simulated transitions.


πŸ‘‘ SEE ALSO πŸ‘‘

Go read these besties, for real:

This man page is ported from the authoritative documentation at: https://skarnet.org/software/s6-rc/s6-rc.html


πŸ’œ AUTHORS πŸ’œ

  • Laurent Bercot
  • Alexis <flexibeast@gmail.com> (man page port)

πŸ’– Footer tea: this one's from Debian, dated January 12, 2026, and it's the S6-RC(8) man page. Bye bestie, go flex those service transitions! πŸ’…

πŸ“¦ where this page comes from