Writing Init Scripts

From Alpine Linux
Revision as of 22:00, 12 September 2025 by Eznix86 (talk | contribs) (Add missing docs for start, stop, restart, and Daemon, Forking, Logging)
This material is work-in-progress ...

Do not follow instructions here until this notice is removed.
(Last edited by Eznix86 on 12 Sep 2025.)

Introduction

Alpine Linux uses the OpenRC init system to start services. Don't confuse OpenRC init with our system init (the first process that is executed aka pid 1). Many of the current init.d script found in Alpine Linux are taken from Gentoo. If you want to save time you could search Gentoo's repository for an existing initscript for your service. You can also check Gentoo's wiki for some additional OpenRC information.

NOTE: OpenRC recently added documentation on how to write proper Init scripts. Make sure you read it!

If you cannot find an init.d script from Gentoo, or you just want to start to write your own init.d scripts, we provide you with some basic information on how to write simple OpenRC init scripts.

Primary information about the OpenRC format can be found in the OpenRC man page openrc-run.

apk add openrc-doc
man openrc-run

Things to avoid

  • Prefer standard OpenRC variables such as command_args, command_user etc. (see openrc-run(8)); do not create unnecessary config variables like FOO_OPTS, FOO_USER etc. If you want to predefine the default value in init.d script, use common idiom : ${command_user=foo}.
  • Use snake_case for naming extra configuration variables (to be consistent with the OpenRC variables and other init scripts in Alpine). Do not prefix them with the service name, try to be consistent with existing init scripts (e.g. cfgfile, cfgdir, logfile, cachedir, listen_on, start_wait, …).

Minimal Templates

Every init.d script you write needs to start with a shebang like:

#!/sbin/openrc-run

Services relying on OpenRC exclusively

#!/sbin/openrc-run

command=/path/to/command

Services supervised by s6

Notes:

  • Install and configure the s6-scan service to start on system boot
  • Exclude start(), stop() and status() functions in order for s6 supervision to work reliably. OpenRC has built-in equivalent functions which invoke the necessary s6 commands.
  • Include a depend() stanza to ensure that the s6-svscan service is already running.
  • Add a start_pre() stanza to symlink the service directory into the scan directory, because the /etc/init.d/bootmisc scripts cleans out the /run directory on system boot.
#!/sbin/openrc-run

name="foo"
supervisor="s6"
s6_service_path="${RC_SVCDIR}/s6-scan/${name}"

depend() {
    need s6-svscan
}

start_pre() {
    if [ ! -L "${RC_SVC_DIR}/s6-scan/${name}" ]; then
        ln -s "/path/to/${name}/service/dir" "${RC_SVCDIR}/s6-scan/${name}"
    fi
}

The rest of the below basic example could be omitted, but that would most probably leave you with an non working initd script.

Basic example

#!/sbin/openrc-run
  
name=$RC_SVCNAME
cfgfile="/etc/$RC_SVCNAME/$RC_SVCNAME.conf"
command="/usr/bin/my_daemon"
command_args="--my-daemon-args"
command_user="my_system_user"
pidfile="/run/$RC_SVCNAME/$RC_SVCNAME.pid"
start_stop_daemon_args="--args-for-start-stop-daemon"
command_background="yes"

depend() {
        need net
}

start_pre() {
        checkpath --directory --owner $command_user:$command_user --mode 0775 \
                /run/$RC_SVCNAME /var/log/$RC_SVCNAME
}

start, stop, restart functions

OpenRC defined a few basic functions ie: start, stop, restart. These functions are defined by default but can be overwritten by defining your own set of functions. This is generally only necessary if you want to do something special which is not provided by the default start/stop/restart implementations.

start

start() {
    ebegin "Starting mydaemon"
    start-stop-daemon --start \
        --exec /usr/sbin/mydaemon \
        --pidfile /var/run/mydaemon.pid \
        -- \
        --args-for-mydaemon
    eend $?
}

stop


stop() {
    ebegin "Stopping mydaemon"
    start-stop-daemon --stop \
        --exec /usr/sbin/mydaemon \
        --pidfile /var/run/mydaemon.pid
    eend $?
}

restart


restart() {
    ebegin "Restarting mydaemon"
    svc_stop
    svc_start
    eend $?
}

Daemon, Forking, Logging

If your daemon supports running in the foreground, set command_background="yes" and add a --no-fork or --foreground option to command_args. This prevents the daemon from forking itself into the background, so OpenRC can handle backgrounding.

When you use command_background="yes", you must also set a pidfile. This allows OpenRC to track and manage the daemon process correctly.

If your daemon does not support running in the foreground, you can use start-stop-daemon directly in your start() function to launch the service and manage forking and pidfiles manually.

For logging, use a logfile variable if your daemon supports it, or redirect output in your start() function.

Example for a daemon that supports foreground mode:

command_args="--no-fork --logfile /var/log/my_daemon.log"
command_background="yes"
pidfile="/run/my_daemon.pid"

Example for a daemon that does not support foreground mode, using start-stop-daemon:

start() {
    ebegin "Starting mydaemon"
    start-stop-daemon --start \
        --exec /usr/bin/mydaemon \
        --pidfile /run/mydaemon.pid \
        --background \
        --make-pidfile \
        -- \
        --logfile /var/log/mydaemon.log
    eend $?
}