S6-BACKGROUND-WATCH(8)

πŸ“¦ version 2.15.1.0, in nixpkgs unstable

πŸ’… s6-background-watch β€” bestie edition

okay so this is the s6-background-watch page, from section 8 of the System Manager's Manual, and it's literally the "make your auto-backgrounding daemon behave" hack you've been looking for πŸŽ€


✨ NAME ✨

s6-background-watch β€” spawn an auto-backgrounding daemon that writes a pidfile, no cap πŸ’…


🌸 SYNOPSIS 🌸

s6-background-watch [-t timeout] [-d notif] pidfile prog...

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so s6-background-watch spawns an auto-backgrounding daemon that writes a pidfile. Then it reads that pidfile and transmits the signals it receives to the corresponding process. It dies when the corresponding process dies, with an approximation (cf. execline-exitcodes(7)) of its exit code. iconic behavior, honestly 🫢

s6-background-watch is meant as a workaround to auto-backgrounding daemons. Note the word workaround β€” not a solution, because the actual solution is to fix the daemon so it runs without backgrounding itself! 😭 It should be run on the command line of a run script, right before the invocation of the auto-backgrounding daemon.

s6-background-watch only works on Linux (or any systems implementing prctl with PR_SET_CHILD_SUBREAPER (cf. PR_SET_CHILD_SUBREAPER(2const))) and the BSDs (or any system implementing procctl with PROC_REAP_ACQUIRE (cf. procctl(2)) or kevent with EVFILT_PROC and NOTE_EXIT fflag (cf. kqueue(2))). Most modern systems will implement one of these, so like, vibe check your OS first πŸ’€

s6-background-watch spawns prog... as a child.

prog... is expected to fork. The child process is supposed to be long-lived, i.e. the child is the daemon that is to be supervised.

The parent is expected to write the child's pid to the pidfile file.

The parent is expected to exit 0 when the child is ready.

Once the parent has exited, s6-background-watch reads pidfile to get the pid of the daemon.

s6-background-watch transmits all the signals it receives to the daemon. Non-catchable signals, such as SIGKILL, will obviously not work, so β€” don't do this πŸ™ˆ

s6-background-watch exits when the daemon exits, with an approximation (cf. execline-exitcodes(7)) of the daemon's exit code. (In most cases it will be the exact same exit code, slay.)

πŸ’œ Notes πŸ’œ

s6-background-watch acts as a proxy between s6-supervise(8) and the forking daemon. It stays in the foreground, keeping s6-supervise happy, and it keeps track of the daemon as best as it can, transmitting it signals. It is a better iteration of s6-fghack(8), which is obsolescent. However β€” and this is the part where we get real β€” it is not perfect, because it cannot be:

  • s6-svc -k (cf. s6-svc(8)), i.e. SIGKILL, is very bad. It will kill s6-background-watch without killing the backgrounded daemon, and all links to the daemon are lost. If the service restarts, it will probably fail, because the previous instance of the daemon is still alive. You may be able to mitigate that with the use of a lock-fd file (cf. s6-service-directory(7)). like, genuinely be careful here πŸ”₯
  • The race condition inherent to pidfiles is there: between the moment the daemon writes its pidfile and the moment s6-background-watch reads it and attempts to track it, the daemon could have died and another pid taken its place. The design of s6-background-watch makes this race window minimal (the system has to loop over the whole pid space between the moment the pidfile is written and the moment s6-background-watch reads it, which is right afterwards, so it's not as bad as most uses of pidfiles) β€” but the race theoretically still exists. not me being honest about theoretical problems 😭
  • In order to detect the death of a process that isn't a direct child, which isn't a functionality guaranteed by POSIX, OS-specific mechanisms have to be used, and not all the supported OSes have such mechanisms. If the OS has no usable mechanism for this, s6-background-watch will immediately exit with an error message.

πŸŽ€ OPTIONS πŸŽ€

  • -t timeout, --timeout-ready=timeout β€” if the parent daemon has not exited after timeout milliseconds, kill it and exit. By default, there is no timeout, and s6-background-watch will wait as long as needed. patient queen energy ⭐
  • -d notif, --notification-fd=notif β€” write a newline to file descriptor notif when the parent has exited 0 and s6-background-watch has successfully read pidfile. This is useful when the daemon follows the convention that the child is ready when the parent exits: use the -d option in conjunction with the notification-fd file, and s6-supervise(8) will accurately report the daemon's readiness. communication, bestie πŸ’–

πŸ”₯ EXIT STATUS πŸ”₯

  • 100 β€” Bad usage. (you typed something cursed)
  • 111 β€” System call failed. This is generally a temporary error, or indicates a problem with the underlying system.
  • 112 β€” Functionality not supported. You cannot run s6-background-watch on this OS, because it lacks the necessary features. wrong OS, bestie πŸ’€
  • 128+n β€” The parent daemon was killed with signal n. In particular, 137 can happen if the -t option has been given and the parent daemon times out: s6-background-watch kills it with SIGKILL.
  • n β€” The child daemon exited with code n.

🌈 SEE ALSO 🌈

kqueue(2), procctl(2), PR_SET_CHILD_SUBREAPER(2const), execline-exitcodes(7), execline-exitcodes(7), s6-service-directory(7), s6-fghack(8), s6-supervise(8), s6-supervise(8), s6-svc(8)

(yes, execline-exitcodes(7) and s6-supervise(8) are each listed twice β€” that's how the original rolls, we don't edit the source, periodt 🫢)

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


πŸ‘‘ AUTHORS πŸ‘‘

  • Laurent Bercot
  • Lennart Jablonka <humm@ljabl.com> (man page port)

that's the whole thing, bestie πŸ’œ this page came from the Debian packaging of the manual, dated October 22, 2025 β€” slay, go supervise something 🧚✨

πŸ“¦ where this page comes from