- Runlevels, defined per service
- One-shot tasks, services (daemons), or SysV init start/stop scripts
- Runparts and
- Process supervision similar to systemd
- Sourcing environment files
- Conditions for network/process/custom dependencies
- Pre/Post script actions
- Tooling to enable/disable services
- Built-in getty
- Built-in watchdog, with support for hand-over to watchdogd
- Built-in support for Debian/BusyBox
- Cgroups v2, both configuration and monitoring in
- Plugin support for customization
- Proper rescue mode with bundled
suloginfor protected maintenance shell
Some of these feature are presented below, for more, see the online documentation.
The GitHub finit/contrib/ section, also part of the release tarball, include sample configurations for Debian, Void, and Alpine Linux.
/etc/finit.conf is for Alpine Linux. The release tarball
contains the recommended (and fairly up to date) “split layout” in the
contrib/alpine section. A split layout is where each
service, or set of related services, are defined in their own
file in the
/etc/finit.d directory. Available, but not yet enabled,
services can be placed in
/etc/finit.d/available and enabled by an
operator using the initctl tool.
# Runlevel to start after bootstrap, 'S', default: 2 runlevel 2 # Max file size for each log file: 100 kiB, rotate max 4 copies: # log => log.1 => log.2.gz => log.3.gz => log.4.gz log size=100k count=4 # Services to be monitored and respawned as needed service [S12345] watchdogd -L -f -- System watchdog daemon service [S12345] syslogd -n -b 3 -D -- System log daemon service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon service  env:-/etc/conf.d/lldpd lldpd -d $LLDPD_OPTS -- LLDP daemon (IEEE 802.1ab) # The BusyBox ntpd does not use syslog when running in the foreground # So we use this trick to redirect stdout/stderr to a log file. The # log file is rotated with the above settings. The condition declares # a dependency on a system default route (gateway) to be set. A single # <!> at the beginning means ntpd does not respect SIGHUP for restart. service  log:/var/log/ntpd.log <!net/route/default> ntpd -n -l -I eth0 -- NTP daemon # For multiple instances of the same service, add :ID somewhere between # the service/run/task keyword and the command. service :80  merecat -n -p 80 /var/www -- Web server service :8080  merecat -n -p 8080 /var/www -- Old web server # Alternative method instead of below runparts, can also use /etc/rc.local #task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap #task [S] /etc/init.d/acpid start -- Starting ACPI Daemon #task [S] /etc/init.d/kbd start -- Preparing console # Run start scripts from this directory # runparts /etc/start.d # Virtual consoles run BusyBox getty, keep kernel default speed tty  /sbin/getty -L 0 /dev/tty1 linux tty  /sbin/getty -L 0 /dev/tty2 linux tty  /sbin/getty -L 0 /dev/tty3 linux # Use built-in getty for serial port and USB serial tty  /dev/ttyAMA0 noclear nowait tty  /dev/ttyUSB0 noclear # Just give me a shell, I need to debug this embedded system! #tty  @console noclear nologin #tty  notty #tty  rescue
service stanza, as well as
run and others are described
in full in doc/config.md. Here’s a quick overview of some of the
most common components needed to start a UNIX daemon:
service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Daemon daemon ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | `-- Optional description | | | | | | `----------- Daemon arguments | | | | | `-------------------------- Path to daemon | | | | `---------------------------------------------------- Optional env. file | | | `-------------------------------------------------------- Redirect output to log | | `--------------------------------------------------------------- Optional conditions | `---------------------------------------------------------------------- Optional Runlevels `------------------------------------------------------------------------------ Monitored application
Some components are optional: runlevel(s), condition(s) and description, making it easy to create simple start scripts and still possible for more advanced uses as well:
service /usr/sbin/sshd -D
Dependencies are handled using conditions. One of the most common conditions is to wait for basic networking to become available:
service <net/route/default> nginx -- High performace HTTP server
Here is another example where we instruct Finit to not start BusyBox
syslogd has started properly. Finit waits for
to create its PID file, by default
service  log <!pid/syslogd> ntpd -n -N -p pool.ntp.org service [S12345] syslogd -n -- Syslog daemon
log keyword, BusyBox
stderr for logging when
run in the foreground. With
log Finit redirects
to the system log daemon using the command line
A service, or task, can have multiple dependencies listed. Here we wait
syslogd to have started and basic networking to be up:
service  log <pid/syslogd,net/route/default> ntpd -n -N -p pool.ntp.org
If either condition fails, e.g. loss of networking,
ntpd is stopped
and as soon as it comes back up again
ntpd is restarted automatically.
Note: Make sure daemons do not fork and detach themselves from the
controlling TTY, usually an
-f flag, or
-D as in the case
of OpenSSH above. If it detaches itself, Finit cannot monitor it and
will instead try to restart it.
Start, monitor and restart services should they fail.
Finit supports external getty but also comes with a limited built-in
Getty, useful for really small systems. A getty sets up the TTY and
waits for user input before handing over to
/bin/login, which is
responsible for handling the actual authentication.
tty  /dev/tty1 nowait linux tty  /dev/ttyAMA0 noclear vt100 tty  /sbin/getty -L /dev/ttyAMA0 vt100
Users of embedded systems may want to enable automatic serial console
with the special
@console device. This works regardless weather the
ttyMXC0, or anything else. Finit
figures it out by querying sysfs:
tty  @console linux noclear
Notice the optional
nologin flags. The
latter is for skipping the login process entirely. For more information,
Support for SysV init-style runlevels is available, in the same
minimal style as everything else in Finit. The
 syntax can be
applied to service, task, run, and TTY stanzas.
Reserved runlevels are 0 and 6, halt and reboot, respectively just like
SysV init. Runlevel 1 can be configured freely, but is recommended to
be kept as the system single-user runlevel since Finit will not start
networking here. The configured
runlevel NUM from
is what Finit changes to after bootstrap, unless ‘single’ (or ‘S’) is
given on the kernel cmdline, in which case runlevel 1 is started.
All services in runlevel S(0) are started first, followed by the desired
run-time runlevel. Run tasks in runlevel S can be started in sequence
run [S] cmd. Changing runlevels at runtime is done like any
other init, e.g. init 4, but also using the more advanced
Plugins can extend the functionality of Finit and hook into the
different stages of the boot process and at runtime. Plugins are
written in C and compiled into a dynamic library loaded automatically by
finit at boot. A basic set of plugins are bundled in the
Hook into the boot at predefined points to extend Finit
Listen to external events and control Finit behavior/services
Extensions and functionality not purely related to what an
needs to start a system are available as a set of plugins that either
hook into the boot process or respond to various I/O.
For more information, see doc/plugins.md.
By default, Finit monitors
registering any changes to
.conf files. To activate a change the user
initctl reload, which reloads all modified files, stops any
removed services, starts new ones, and restarts any modified ones, with
SIGHUP if the process supports it.
For some use-cases the extra step of calling
initctl reload creates an
unnecessary overhead, which can be removed at build-time using:
Runparts & /etc/rc.local
At the end of the boot, when all bootstrap (
S) tasks and services have
started, but not networking, Finit calls its built-in [run-parts(8)]
command on any configured
runparts <DIR> directory. This happens just
before changing to the configured runlevel (default 2). (Networking is
enabled just prior to changing from single user mode.)
Right after the runlevel change when all services have started properly,
/etc/rc.local is called.
No configuration stanza in
/etc/finit.conf is required for
If it exists and is an executable shell script Finit calls it at the very
end of the boot, before calling the
HOOK_SYSTEM_UP. See more on hooks
in doc/plugins.md, and about the system bootstrap
Basic support for runlevels is included in Finit from v1.8. By
default all services, tasks, run commands and TTYs listed without a set
of runlevels get a default set
 assigned. The default runlevel
after boot is 2.
Finit supports runlevels 0-9, and S, with 0 reserved for halt, 6 reboot and S for services to only run at bootstrap. Runlevel 1 is the single user level, where usually no networking is enabled. In Finit this is more of a policy for the user to define. Normally only runlevels 1-6 are used, and even more commonly, only the default runlevel is used.
To specify an allowed set of runlevels for a
[NNN] to your
/etc/finit.conf, like this:
service [S12345] syslogd -n -x -- System log daemon run [S] /etc/init.d/acpid start -- Starting ACPI Daemon task [S] /etc/init.d/kbd start -- Preparing console service [S12345] <pid/syslogd> klogd -n -x -- Kernel log daemon tty  /dev/tty1 tty  /dev/tty2 tty  /dev/tty3 tty  /dev/tty4 tty  /dev/tty5 tty  /dev/tty6
In this example syslogd is first started, in parallel, and then acpid is called using a conventional SysV init script. It is called with the run command, meaning the following task command to start the kbd script is not called until the acpid init script has fully completed. Then the keyboard setup script is called in parallel with klogd as a monitored service.
Again, tasks and services are started in parallel, while run commands are called in the order listed and subsequent commands are not started until a run command has completed. Also, task and run commands are run in a shell, so pipes and redirects can be used.
The following examples illustrate this. Bootstrap task and run commands
are also removed when they have completed,
initctl show will not list
task [S] echo "foo" | cat >/tmp/bar run [S] echo "$HOME" >/tmp/secret
Switching between runlevels can be done by calling init with a single
argument, e.g. init 5, or using
initctl runlevel 5, both
switch to runlevel 5. When changing runlevels Finit also automatically
.conf files in the
/etc/finit.d/ directory. So if you
want to set a new system config, switch to runlevel 1, change all config
files in the system, and touch all
.conf files in
before switching back to the previous runlevel again — that way Finit
can both stop old services and start any new ones for you, without
rebooting the system.
Rebooting & Halting
Traditionally, rebooting and halting a UNIX system is done by changing
its runlevel. Finit comes with its own tooling providing:
suspend, but also the
detailed in the next section.
For compatibility reasons Finit listens to the same set of signals as BusyBox init. This is not 100% compatible with SysV init, but clearly the more common combination for Finit. For more details, see doc/signals.md.
Commands & Status
Finit also implements a modern API to query status, and start/stop
initctl tool does
not return until the given command has fully completed.
alpine:~# initctl help Usage: initctl [OPTIONS] [COMMAND] Options: -b, --batch Batch mode, no screen size probing -c, --create Create missing paths (and files) as needed -f, --force Ignore missing files and arguments, never prompt -1, --once Only one lap in commands like 'top' -p, --plain Use plain table headings, no ctrl chars -q, --quiet Silent, only return status of command -t, --no-heading Skip table headings -v, --verbose Verbose output -h, --help This help text Commands: debug Toggle Finit (daemon) debug help This help text version Show Finit version ls | list List all .conf in /etc/finit.d create <CONF> Create .conf in /etc/finit.d/available delete <CONF> Delete .conf in /etc/finit.d/available show <CONF> Show .conf in /etc/finit.d/available edit <CONF> Edit .conf in /etc/finit.d/available touch <CONF> Change .conf in /etc/finit.d/available enable <CONF> Enable .conf in /etc/finit.d/available disable <CONF> Disable .conf in /etc/finit.d/enabled reload Reload *.conf in /etc/finit.d (activate changes) cond set <COND> Set (assert) user-defined condition +usr/COND cond clear <COND> Clear (deassert) user-defined condition -usr/COND cond status Show condition status, default cond command cond dump Dump all conditions and their status log [NAME] Show ten last Finit, or NAME, messages from syslog start <NAME>[:ID] Start service by name, with optional ID stop <NAME>[:ID] Stop/Pause a running service by name reload <NAME>[:ID] Reload service by name (SIGHUP or restart) restart <NAME>[:ID] Restart (stop/start) service by name status <NAME>[:ID] Show service status, by name status Show status of services, default command cgroup List cgroup config overview ps List processes based on cgroups top Show top-like listing based on cgroups runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot reboot Reboot system halt Halt system poweroff Halt and power off system suspend Suspend system
For services not supporting
<!> notation in the .conf
file must be used to tell Finit to stop and start it on
runlevel changes. If
<> holds more conditions, these will
also affect how a service is maintained.
Note: even though it is possible to start services not belonging in the current runlevel these services will not be respawned automatically by Finit if they exit (crash). Hence, if the runlevel is 2, the below Dropbear SSH service will not be restarted if it is killed or exits.
alpine:~# initctl PID IDENT STATUS RUNLEVELS DESCRIPTION ====================================================================== 1506 acpid running [--2345----] ACPI daemon 1509 crond running [--2345----] Cron daemon 1489 dropbear running [--2345----] Dropbear SSH daemon 1511 klogd running [S12345----] Kernel log daemon 1512 ntpd running [--2345----] NTP daemon 1473 syslogd running [S12345----] Syslog daemon alpine:~# initctl -v PID IDENT STATUS RUNLEVELS COMMAND ====================================================================== 1506 acpid running [--2345----] acpid -f 1509 crond running [--2345----] crond -f -S $CRON_OPTS 1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS 1511 klogd running [S12345----] klogd -n $KLOGD_OPTS 1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS 1473 syslogd running [S12345----] syslogd -n
The environment variables to each of the services above are read from,
in the case of Alpine Linux,
/etc/conf.d/. Other distributions may
have other directories, e.g., Debian use
Finit is capable of running on both desktop/server systems with udev and
embedded systems that usually come with BusyBox mdev. Some systems have
systemd-udev or eudev today instead of the original udev, Finit probes
for all of them at runtime and expects
/dev/ to be a writable file
devtmpfs. It is also possible to run on a statically set
/dev if needed. It is however not a good idea to have both udev
and mdev installed at the same time, this will lead to unpredictable
At boot Finit calls either
udevd to populate
/dev, this is
done slightly differently and on systems with udev you might want to add
the following one-shot task early in your
run [S] udevadm settle --timeout=120 -- Waiting for udev
Finit has a built-in Getty for TTYs, but requires a working
/bin/sh, if no TTYs are configured in
For a fully operational system
/tmp must be set up
/etc/fstab – which is iterated over at boot.
Please file bug reports, clone it, or send pull requests for bug fixes and proposed extensions using GitHub: