Files
finit/doc/config/service-opts.md
T
Joachim Wiberg b9ad9bcb21 doc: convert the documentation to the block format
The syntax overview no longer describes a line-based format, since that
is not what the rest of the documentation shows.  It now covers the
grammar, the two naming conventions, the nine aliases, and the leading
'-' on a path, and it says plainly that both formats are still read and
told apart per file by content.  Without that, a reader with an
existing configuration is left wondering what happened to it.

service-opts.md was a list of modifiers to place between a directive
and its command, so it needed rewriting rather than translating: there
are no positions left to describe.  It is now grouped by what the
settings do.

conditions.md needed correcting.  It presented '!' as a condition
prefix alongside '~'.  It is neither a condition nor a negation, it is
a flag on the block that means one thing on a service and another on a
run or task, so it is spelled reload-signal and required here, and the
page maps the old form to both.

Two things the pages claimed are not true.  The kill delay range is
1-300, not 1-60, and stop and reload scripts are no longer run without
a timeout.

ChangeLog.md keeps its line-based examples.  Those sit in historical
release entries, and rewriting them in a syntax that did not exist at
the time would misdate the format.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:31 +02:00

6.9 KiB

Service Options

The run, task, tty, service and sysv blocks take settings as keys. This section lists them with their limitations.

The name of a service, shown by the initctl tool, is the block title. It defaults to the basename of the executable if the title is omitted:

service sshd {
    command = "/usr/sbin/sshd -D"
}

For multiple instances of a service, with the same name, add an identifier after a colon to prevent Finit from replacing previous instances:

service ssdp:eth1 { command = "ssdpd eth1" }
service ssdp:eth2 { command = "ssdpd eth2" }

The initctl tool will list these two services as:

  • ssdp:eth1
  • ssdp:eth2

Conflicting services that must be prevented from starting, use the conflicts key:

service udevd { command = "udevd" }
run mdev {
    runlevel  = "S"
    conflicts = { "udevd" }
    command   = "mdev -s"
}

The list may name several, and an instance is named the same way it is declared:

service abc:1 { command = "abc 1" }
service abc:2 { command = "abc 2" }
service cde   { conflicts = { "abc:1", "abc:2" }  command = "cde" }

If a service should not be automatically started, set manual-start. It can then be started at any time with initctl start <service>:

service lldpd {
    manual-start = true
    command      = "/usr/sbin/lldpd -d"
}

Other run/task/service settings are:

Restarting

Services are automatically started, restarted, and stopped, depending on the configuration and conditions. Within the confines of that the following settings are available:

  • restart-max = NUM -- number of times Finit tries to restart a crashing service, default: 10, max: 255. When this limit is reached the service is marked crashed and must be restarted manually with initctl restart NAME
  • restart-sec = SEC -- number of seconds before Finit tries to restart a crashing service, default: 2 seconds for the first five retries, then back-off to 5 seconds. The maximum of this configured value and the above (2 and 5) will be used
  • restart = "always" -- no upper limit on the number of times Finit tries to restart a crashing service
  • restart = "never" -- do not restart on failures. false is accepted as a synonym, and true selects the default policy
  • respawn = true -- bypasses the restart mechanism completely, allowing endless restarts. Exiting is treated as normal work rather than failure, so the service is restarted at once instead of being counted and delayed. This is how a tty behaves; it is not what service was designed for, so it is not the default
  • remain-after-exit = true -- for run and task only. Prevents the task from re-running on runlevel re-entry and ensures the exec-stop-post script runs when the task is explicitly stopped or leaves its valid runlevels. This is systemd's RemainAfterExit=. See Task and Run for more details
  • oncrash = "reboot" -- when all retries have failed, and the service has crashed, the system is rebooted
  • oncrash = "script" -- similarly, but instead of rebooting, call the exec-stop-post script with exit code crashed, see below

Stopping and reloading

When stopping a service, either manually or when moving to another runlevel, Finit starts by sending SIGTERM to let the process shut down gracefully. If it has not been collected within 3 seconds, SIGKILL follows. Both ends of that are configurable:

  • stop-signal = "SIGPWR" -- send this instead of SIGTERM
  • stop-timeout = 10 -- seconds to wait before SIGKILL, 1-300

Some services need more than a signal:

  • exec-stop = "script [args]" -- run instead of sending the stop signal, and instead of stop for sysv
  • exec-reload = "script [args]" -- run instead of sending SIGHUP. Like systemd, Finit sets $MAINPID as a convenience to scripts, which in effect also allows exec-reload = "kill -HUP $MAINPID"

If a daemon cannot be reloaded with a signal at all, say so and Finit restarts it instead:

service dropbear {
    reload-signal = "none"
    command       = "/usr/sbin/dropbear -R -F"
}

SIGHUP is the default and the only other accepted value, with or without the SIG prefix and in any case.

Scripts

Services, including the sysv variant, support six lifecycle scripts:

  • exec-start-pre -- called before the sysv/service is started
  • exec-start-ready -- called when the sysv/service is ready
  • exec-stop -- called instead of the stop signal
  • exec-stop-post -- called after the sysv/service has stopped
  • exec-reload -- called instead of SIGHUP
  • exec-cleanup -- called when run/task/sysv/service is removed

Each takes a matching -timeout, in seconds, 0-3600, after which Finit kills the script. It defaults to the stop-timeout value and can be disabled by setting it to zero:

service foo {
    exec-start-pre         = "/etc/foo/pre.sh"
    exec-start-pre-timeout = 10
    command                = "/usr/sbin/foo"
}

These scripts run as the same user and group as the service itself, with any envfile sourced. They are executed from the $HOME of the given user. The scripts are not called with any argument, but get a set of environment variables:

  • SERVICE_IDENT=foo:1
  • SERVICE_NAME=foo
  • SERVICE_ID=1

The exec-stop-post script is called with an additional set of environment variables. Yes, the text is correct, the naming was an accident:

  • EXIT_CODE=[exited,signal,crashed]: normal exit, signaled, or crashed
  • EXIT_STATUS=[num,SIGNAME]: set to one of exit status code from the program, if it exited normally, or the signal name (HUP, TERM, etc.) if it exited due to signal

When a run/task/sysv/service is removed (disable + reload) it is first stopped and then removed from the runlevel. The exec-stop-post script always runs when the process has stopped, and exec-cleanup runs when the stanza has been removed from the runlevel.

Important

These script actions are intended for setup, cleanup, and readiness notification. A script that outlives its timeout is killed, so pick one that suits the work, or set it to zero to opt out and take responsibility for the script terminating.