A service manager and init system written in Go. The core is a port of dinit, with features layered in from runit, s6-linux-init, and OpenRC.
slinit can run as PID 1 (init system) or as a user-level service manager. It uses a dinit-compatible configuration format and manages services with dependency tracking, automatic restart, and process lifecycle management. Admins moving from any of the four upstreams should keep their muscle memory:
- dinit: service-description format, dep types, state machine, and
slinitctlverbs are 1:1 with dinit. - runit:
finish-command,ready-check-command,pre-stop-hook,env-dir,control-command-<SIGNAL>,chroot,new-session,lock-file, log rotation/filtering/processor, down-file marker,oncecommand. - s6-linux-init: catch-all logger, TAI64N/ISO timestamps,
scheduled shutdown + cancel, wall messages,
/etc/shutdown.allowaccess control, global boot-time rlimits, container mode with exit/halt codes + ready-fd, SysV compat symlinks,slinit-init-makergenerator,slinit-nukeemergency, RT-signal container shutdown. - OpenRC:
rc-service/rc-update/rc-statusCLI shims,/etc/rc.conf+/etc/conf.d/<service>sourcing for init.d scripts, named-runlevel dispatch (init default|single|nonetwork|...), init.d/LSB auto-detection.
Runlevels, where present, are pure UX aliases over the dependency graph — slinit does not introduce a second state machine or config format to accommodate them.
- Service types: process, scripted, bgprocess, internal, triggered
- Dependency management: 6 dependency types (regular, waits-for, milestone, soft, before, after)
- Process lifecycle: SIGTERM with configurable timeout, SIGKILL escalation
- Auto-restart: configurable restart policy with rate limiting and smooth recovery
- Dinit-compatible config: key=value service description files
- Environment substitution:
$VAR,${VAR},${VAR:-default},${VAR:+alt},$$escape in config files - Word-splitting expansion:
$/VARsplits variable value on whitespace into multiple command args - Service templates:
name@argumentpattern with$1substitution in config - Config includes:
@includeand@include-optdirectives for modular config - Runit-inspired features: finish-command, ready-check-command, pre-stop-hook, env-dir, control-command, chroot, new-session, lock-file, close-fds, log rotation/filtering/processor, down-file marker
- Control socket: binary protocol (v6) over Unix domain socket for runtime management
- slinitctl CLI: list, start, stop, wake, release, restart, status, is-started, is-failed, trigger, untrigger, signal, pause, continue, once, reload, unload, unpin, catlog, attach, setenv, unsetenv, getallenv, setenv-global, unsetenv-global, getallenv-global, add-dep, rm-dep, enable, disable, shutdown (with scheduled/cancel/status), graph, dependents, query-name, service-dirs, load-mech, boot-time, analyze
- slinit-check: offline and online config linter (validates executables, paths, dependencies;
--onlinequeries running daemon) - slinit-monitor: event watcher + command executor (
%n/%s/%vsubstitution) - Service aliases:
providesfor alternative name lookup - Consumer pipes:
consumer-ofto pipe output from one service into another - Log output: buffer (in-memory, catlog), file (logfile with permissions/ownership + rotation/filtering), pipe (consumer-of)
- Log rotation: size-based, time-based, max files, log processor script, include/exclude pattern filtering
- Ready notification: pipefd/pipevar readiness protocol for services, ready-check-command polling
- Socket activation: pre-opened listening sockets passed to child (LISTEN_FDS=N convention), supports Unix/TCP/UDP (
tcp:host:port,udp:host:port), multiple sockets via+=, on-demand activation - Hot reload: reload service configuration from disk without restart
- Service unload: remove stopped services from memory
- PID 1 init: console setup, Ctrl+Alt+Del handling, child subreaper, orphan reaping
- Process attributes: nice, oom-score-adj, rlimits, ioprio, cgroup, cpu-affinity, no-new-privs, capabilities, securebits
- Runtime environment: setenv/unsetenv/getallenv via control socket, env-file loading (with
!clear/!unset/!importmeta-commands), env-dir (runit-style directory) - Process isolation: chroot, new-session (setsid), lock-file (exclusive flock), close-stdin/stdout/stderr
- Service lifecycle hooks: finish-command (post-exit), pre-stop-hook (pre-SIGTERM), control-command (custom signal handlers)
- Pause/continue: SIGSTOP/SIGCONT via
slinitctl pause/continuewith control-command override - Down file:
downmarker file prevents auto-start (cleared by explicitslinitctl start) - Once mode:
slinitctl oncestarts a service without auto-restart - Runtime dependencies: add-dep/rm-dep, enable/disable via control socket
- Enable-via:
@meta enable-viadirective for default enable/disable source service - Push notifications: SERVICEEVENT/ENVEVENT for real-time state and environment tracking
- SIGUSR1 socket reopen: recover control socket when filesystem becomes writable
- slinit-shutdown: standalone shutdown utility (also invocable as slinit-reboot, slinit-halt, slinit-soft-reboot via symlinks)
- Shutdown: orderly service stop, shutdown hooks, process cleanup (SIGTERM/SIGKILL), filesystem sync, reboot/halt/poweroff/kexec/softreboot
- Soft-reboot: restart slinit without rebooting the kernel (with shutdown hooks)
- Kexec reboot: reboot via kexec (skip firmware reinit, requires pre-loaded kernel)
- Container mode:
-o/--containerfor Docker/LXC/Podman (SIGINT/SIGTERM → graceful halt) - Boot failure recovery: interactive prompt or auto-recovery (
-r) when all services stop without shutdown - Multiple boot services:
-t svc1 -t svc2or positional args to start multiple services at boot - Pass control socket:
pass-cs-fdpasses a control connection fd to child processes - Readiness signaling:
starts-rwfs/starts-logflags for filesystem and logging readiness - UTMPX support:
inittab-id/inittab-linefor session tracking, boot logging - /etc/init.d auto-detect: automatic detection of SysV init scripts with LSB header parsing, BSD rc.d support
- Cron-like periodic tasks:
cron-commandwith configurable interval, delay, and on-error behavior - Shutdown info display: periodic reporter of blocking services during shutdown, escalating force shutdown (2nd signal reduces timeout, 3rd sends SIGKILL)
- Parallel start limit: soft concurrency control for service startup (
--parallel-start-limit), slow-threshold filtering - Multi-service shared logger: SharedLogMux multiplexes N service outputs into a single logger stdin with
[service-name]line prefixes - Virtual TTY: screen-like attach/detach for services via PTY allocation, ring buffer scrollback, Unix socket client multiplexing (
slinitctl attach) - Boot-time clock guard: prevents clock regression on systems without RTC / dead CMOS battery (compile-time floor + persistent timestamp file, similar to systemd-timesyncd)
- Dual mode: system init (PID 1) or user-level service manager
- Offline enable/disable:
--offlinemode creates/removes waits-for.d symlinks without a running daemon - Dinit naming compat:
rlimit-addrspace,run-in-cgroup,consumer-of =all supported as aliases - s6-linux-init features:
- Catch-all logger capturing early-boot stdout/stderr (
--catch-all-log,-Bto disable) - TAI64N / ISO-8601 / wallclock / none log timestamps (
--timestamp-format) - Scheduled shutdown (
shutdown +5,shutdown HH:MM) with cancel (shutdown -c) and status - Wall broadcasts to logged-in users on shutdown (disable with
--no-wall) /etc/slinit/shutdown.allow//etc/shutdown.allowaccess control for signal-driven shutdown- Configurable
SIGTERM→SIGKILLgrace period (--shutdown-grace) - Global rlimits at boot, inherited by all services (
--rlimits nofile=65536,core=0,...) - RT-signal container shutdown (SIGRTMIN+3..+6 → halt/poweroff/reboot/kexec)
- UTMPX logout records for every active session + RUN_LVL shutdown boundary in wtmp
- Kernel cmdline snapshot to
/run/slinit/kcmdline(--kcmdline-dest) /runtmpfs staging modes (--run-mode=mount|remount|keep)- Configurable devtmpfs mount point (
--devtmpfs-path, empty disables) /sbin/halt,/sbin/poweroff,/sbin/rebootcompat via argv[0] dispatch
- Catch-all logger capturing early-boot stdout/stderr (
- OpenRC UX compat:
rc-service <svc> <action>— translates toslinitctl start|stop|restart|status|...rc-update add|del <svc> [runlevel]— models runlevels asrunlevel-<name>servicesrc-status [runlevel]— lists services grouped by runlevel dep graph- Init.d scripts source
/etc/rc.conf+/etc/conf.d/<name>automatically viash -cwrapper - Named runlevel dispatch:
init default|single|nonetwork|boot|sysinit→ startrunlevel-<name>
- SysV compat:
init 0→ poweroff,init 6→ reboot,init N(1..5) → start runlevel-N - Standalone binaries:
slinit-init-maker(bootable layout generator),slinit-nuke(emergencykill -1),slinit-shutdown(orderly shutdown shim, also invocable asslinit-reboot/slinit-halt/slinit-soft-rebootsymlinks)
# Core daemon + control CLI
go build ./cmd/slinit
go build ./cmd/slinitctl
# Companion utilities
go build ./cmd/slinit-check # offline/online config linter
go build ./cmd/slinit-monitor # event watcher + command executor
go build ./cmd/slinit-shutdown # standalone shutdown utility
go build ./cmd/slinit-init-maker # bootable service-dir generator
go build ./cmd/slinit-nuke # emergency kill-all
go build ./cmd/slinit-mount # autofs lazy-mount helper
go build ./cmd/slinit-checkpath # path-validation helper
# OpenRC compat shims
go build ./cmd/rc-service
go build ./cmd/rc-update
go build ./cmd/rc-status
# Or build everything at once
go build ./...
# Optional compat symlinks:
ln -s slinit-shutdown slinit-reboot
ln -s slinit-shutdown slinit-halt
ln -s slinit-shutdown slinit-soft-reboot
# SysV compat (slinit itself handles these via argv[0]):
ln -s slinit /sbin/halt
ln -s slinit /sbin/poweroff
ln -s slinit /sbin/reboot# User mode (default)
./slinit --services-dir /path/to/services
# System mode
./slinit --system --services-dir /etc/slinit.d
# Multiple boot services
./slinit -t network -t web-server -t database
# Container mode
./slinit --container -t myapp| Flag | Description | Default |
|---|---|---|
--services-dir |
Service description directory (comma-separated) | ~/.config/slinit.d (user) or multiple system dirs |
--socket-path |
Control socket path | ~/.slinitctl or /run/slinit.socket |
--system / -m / --system-mgr |
Run as system service manager | false |
--user |
Run as user service manager | true |
-t / --service |
Service to start at boot (repeatable, or use positional args) | boot |
-o / --container |
Run in container mode (Docker/LXC/Podman) | false |
--log-level |
Log level (debug, info, notice, warn, error) | info |
--console-level |
Minimum level for console output | inherits --log-level |
-q / --quiet |
Suppress all but error output | false |
-r / --auto-recovery |
Auto-start recovery service on boot failure (PID 1) |
false |
-e / --env-file |
Environment file to load at startup | |
-F / --ready-fd |
File descriptor to notify when boot service is ready | -1 |
-l / --log-file |
Log to file instead of console | |
-b / --cgroup-path |
Default cgroup base path for services | |
--parallel-start-limit |
Max concurrent service starts (0 = unlimited) | 0 |
--parallel-start-slow-threshold |
Seconds before a starting service is considered "slow" | 10s |
--shutdown-grace |
SIGTERM→SIGKILL grace period during shutdown | 3s |
--no-wall |
Disable wall broadcasts at shutdown | false |
--banner |
Boot banner printed to console (empty disables) | slinit booting... |
--umask |
Initial umask (octal) | 0022 |
-1 / --console-dup |
Duplicate log output to /dev/console even with --log-file |
false |
--catch-all-log |
Path for the early-boot catch-all log | /run/slinit/catch-all.log |
-B / --no-catch-all |
Disable catch-all logger | false |
--timestamp-format |
Log timestamp format (wallclock|iso|tai64n|none) |
wallclock |
--rlimits |
Global rlimits applied to slinit and inherited by services (name=soft[:hard] comma-separated) |
|
--run-mode |
Stage /run at boot: mount (fresh tmpfs), remount (unmount+mount), keep (untouched) |
mount |
--devtmpfs-path |
Mount devtmpfs at this path (empty disables) | /dev |
--kcmdline-dest |
Snapshot /proc/cmdline to this path (empty disables) |
/run/slinit/kcmdline |
-S / --sys |
Override platform detection (docker, lxc, podman, systemd-nspawn, openvz, vserver, rkt, uml, wsl, xen0, xenu, kvm, qemu, vmware, microsoft (Hyper-V), oracle (VirtualBox), bochs, none) |
auto |
--conf-dir |
Override conf.d overlay directories (comma-separated; none disables overlays) |
|
--version |
Show version and exit |
Default service directories (when --services-dir is not set):
- System mode:
/etc/slinit.d,/run/slinit.d,/usr/local/lib/slinit.d,/lib/slinit.d - User mode:
$XDG_CONFIG_HOME/slinit.d(or~/.config/slinit.d),/etc/slinit.d/user,/usr/lib/slinit.d/user,/usr/local/lib/slinit.d/user
Service files use a dinit-compatible format:
# /etc/slinit.d/myservice
type = process
command = /usr/bin/myservice --config /etc/myservice.conf
stop-command = /usr/bin/myservice --stop
stop-timeout = 10
restart = on-failure
restart-delay = 2
restart-limit-count = 3
restart-limit-interval = 60
depends-on: network
waits-for: logging
log-type = buffer
log-buffer-size = 4096Example bgprocess service:
# /etc/slinit.d/mydaemon
type = bgprocess
command = /usr/sbin/mydaemon
pid-file = /run/mydaemon.pid
stop-timeout = 15
depends-on: networkExample service with logfile output:
# /etc/slinit.d/myapp
type = process
command = /usr/bin/myapp
log-type = file
logfile = /var/log/myapp.log
logfile-permissions = 0640
logfile-uid = 1000
logfile-gid = 1000Example service with process attributes and capabilities:
# /etc/slinit.d/worker
type = process
command = /usr/bin/worker
nice = 10
oom-score-adj = 500
ioprio = be:4
cpu-affinity = 0-3
rlimit-nofile = 1024:4096
rlimit-core = unlimited
cgroup = /sys/fs/cgroup/workers
capabilities = cap_net_bind_service,cap_sys_nice
securebits = noroot keep-caps
options = no-new-privs
env-file = /etc/worker.env
run-as = worker:workerExample service with runit-inspired features:
# /etc/slinit.d/webapp
type = process
command = /usr/bin/webapp
finish-command = /usr/local/bin/cleanup.sh
ready-check-command = /usr/bin/curl -sf http://localhost:8080/health
ready-check-interval = 0.5
pre-stop-hook = /usr/local/bin/drain-connections.sh
control-command-HUP = /usr/local/bin/graceful-reload.sh
env-dir = /etc/webapp/env.d
chroot = /srv/webapp
new-session = true
lock-file = /run/webapp.lock
log-type = file
logfile = /var/log/webapp.log
logfile-max-size = 10000000
logfile-max-files = 5
logfile-rotate-time = 86400
log-processor = /usr/bin/gzip
log-exclude = DEBUG
restart = on-failure
depends-on: networkExample consumer pipe (service B reads service A stdout):
# /etc/slinit.d/producer
type = process
command = /usr/bin/generate-data
log-type = pipe
# /etc/slinit.d/consumer
type = process
command = /usr/bin/process-data
consumer-of: producerExample service template (myservice@ base file):
# /etc/slinit.d/myservice@
type = process
command = /usr/bin/myservice --instance $1
working-dir = /var/lib/myservice/${1}
depends-on: networkStart with slinitctl start myservice@web — $1 is replaced with web.
Example multi-service shared logger:
# /etc/slinit.d/central-logger
type = process
command = /usr/bin/multilog t ./log
# /etc/slinit.d/app-one
type = process
command = /usr/bin/app-one
shared-logger = central-logger
# /etc/slinit.d/app-two
type = process
command = /usr/bin/app-two
shared-logger = central-loggerLogger receives lines prefixed: [app-one] ..., [app-two] ....
Example service with virtual TTY:
# /etc/slinit.d/interactive-svc
type = process
command = /usr/bin/myapp
vtty = true
vtty-scrollback = 131072Attach with slinitctl attach interactive-svc (Ctrl+] to detach).
Example service with cron task:
# /etc/slinit.d/worker
type = process
command = /usr/bin/worker
cron-command = /usr/bin/cleanup-temp
cron-interval = 3600
cron-delay = 60
cron-on-error = continueExample with @meta enable-via:
# /etc/slinit.d/optional-svc
type = process
command = /usr/bin/optional
@meta enable-via mygroupslinitctl enable optional-svc will add a waits-for dep from mygroup instead of boot.
| Option | Description |
|---|---|
type |
Service type (process, bgprocess, scripted, internal, triggered) |
command |
Command to run (supports += to append) |
stop-command |
Command to run on stop (scripted, supports +=) |
depends-on: |
Hard dependency |
depends-ms: |
Milestone dependency (must start, then becomes soft) |
waits-for: |
Soft dependency (wait for start/fail) |
before: |
Ordering: start before target |
after: |
Ordering: start after target |
provides |
Alias name for service lookup |
consumer-of / consumer-of: |
Pipe output from named service into this one (= or :) |
restart |
Auto-restart mode (yes, on-failure, no) |
restart-delay |
Seconds to wait before restarting |
restart-limit-count |
Max restarts within interval |
restart-limit-interval |
Interval (seconds) for restart limit |
log-type |
Output logging (buffer, file, pipe, none) |
logfile |
Log file path (when log-type = file) |
log-buffer-size |
Log buffer size in bytes (when log-type = buffer) |
logfile-permissions |
Log file permissions, octal (default 0600) |
logfile-uid |
Log file owner UID |
logfile-gid |
Log file owner GID |
ready-notification |
Readiness protocol (pipefd:N, pipevar:VARNAME) |
socket-listen |
Pre-opened listening socket(s) passed to child (LISTEN_FDS), supports += for multiple, tcp:/udp: prefix |
socket-activation |
Activation mode: immediate (default) or on-demand |
socket-permissions |
Socket file permissions |
socket-uid/gid |
Socket file ownership |
pid-file |
PID file path (bgprocess type) |
start-timeout |
Timeout for service start (seconds) |
stop-timeout |
Timeout for service stop (seconds) |
options |
Service flags (runs-on-console, unmask-intr, no-new-privs, etc.) |
term-signal |
Signal for graceful stop |
working-dir |
Working directory for the process |
run-as |
Run command as user:group |
env-file |
Environment variables file (KEY=VALUE, !clear, !unset, !import) |
env-dir |
Runit-style env directory (one file per var) |
finish-command |
Command run after process exit (before restart) |
ready-check-command |
Polling readiness check (alternative to pipefd) |
ready-check-interval |
Polling interval for ready-check (default 1s) |
pre-stop-hook |
Command run before SIGTERM (receives PID as arg) |
control-command-SIGNAL |
Custom signal handler (e.g., control-command-HUP) |
chroot |
Chroot directory before exec |
new-session |
Create new session (setsid) for the process |
lock-file |
Exclusive flock file (prevents duplicate instances) |
close-stdin |
Close stdin (redirect to /dev/null) |
close-stdout |
Close stdout (redirect to /dev/null) |
close-stderr |
Close stderr (redirect to /dev/null) |
logfile-max-size |
Rotate logfile at this size (bytes) |
logfile-max-files |
Max rotated log files to keep |
logfile-rotate-time |
Rotate logfile at time interval (seconds) |
log-processor |
Command run on each rotated logfile |
log-include |
Regex: only write matching lines to log |
log-exclude |
Regex: drop matching lines from log |
chain-to |
Service to start after this one stops |
nice |
Process scheduling priority (-20..19) |
oom-score-adj |
OOM killer score adjustment (-1000..1000) |
ioprio |
I/O priority class:level (be:4, rt:0, idle) |
cpu-affinity |
CPU affinity mask (0-3, 0 1 2, 0,2,4) |
cgroup |
Cgroup path for the child process |
rlimit-nofile |
File descriptor limit (soft:hard or unlimited) |
rlimit-core |
Core dump size limit (soft:hard or unlimited) |
rlimit-data |
Data segment size limit (soft:hard or unlimited) |
rlimit-as |
Address space limit (soft:hard or unlimited) |
rlimit-addrspace |
Alias for rlimit-as (dinit compat) |
run-in-cgroup |
Alias for cgroup (dinit compat) |
capabilities |
Ambient capabilities (cap_net_bind_service, etc.) |
securebits |
Securebits flags (noroot, keep-caps, etc.) |
inittab-id |
UTMPX inittab ID for session tracking |
inittab-line |
UTMPX inittab line for session tracking |
load-options |
Loader flags (export-passwd-vars, export-service-name) |
@meta enable-via |
Default "from" service for enable/disable |
shared-logger |
Name of shared logger service (multi-service → single logger) |
vtty |
Enable virtual TTY for screen-like attach/detach |
vtty-scrollback |
VirtualTTY scrollback buffer size in bytes (default 64KB) |
cron-command |
Periodic command to execute while service is running |
cron-interval |
Interval between cron executions (seconds) |
cron-delay |
Initial delay before first cron execution (seconds) |
cron-on-error |
Behavior on cron command failure: continue (default) or stop |
@include |
Include another config file (error if not found) |
@include-opt |
Include another config file (ignore if not found) |
| Type | Description |
|---|---|
process |
Long-running daemon managed by slinit |
scripted |
Service controlled by start/stop commands |
internal |
Milestone service with no associated process |
bgprocess |
Self-backgrounding daemon (forks, writes PID file, monitored via polling) |
triggered |
Service that waits for an external trigger before completing startup |
| Directive | Description |
|---|---|
depends-on |
Hard dependency -- start required, stop propagates |
depends-ms |
Milestone dependency -- must start, then becomes soft |
waits-for |
Soft dependency -- waits for start, but failure doesn't propagate |
before |
Ordering -- this service starts before the named service |
after |
Ordering -- this service starts after the named service |
Config values support environment variable expansion:
| Syntax | Description |
|---|---|
$VAR |
Expand variable |
${VAR} |
Expand variable (explicit braces) |
${VAR:-default} |
Use default if VAR is empty/unset |
${VAR:+alt} |
Use alt if VAR is set and non-empty |
$$ |
Literal $ |
$/VAR |
Word-split: expand and split on whitespace into multiple args |
$1 / ${1} |
Service template argument (for name@arg services) |
slinitctl communicates with a running slinit instance via the control socket.
| Flag | Description |
|---|---|
--socket-path, -p |
Control socket path |
--system, -s |
Connect to system service manager |
--user, -u |
Connect to user service manager |
--no-wait |
Suppress output (fire-and-forget) |
--pin |
Pin service in started/stopped state (start/stop) |
--force, -f |
Force stop even with dependents (stop/restart) |
--ignore-unstarted |
Exit 0 if service already stopped (stop/restart) |
--offline, -o |
Offline mode for enable/disable without daemon |
--services-dir, -d |
Service directory for offline mode |
--from <service> |
Source service for enable/disable (default: enable-via or boot) |
--use-passed-cfd |
Use fd from SLINIT_CS_FD env var |
# List all loaded services
slinitctl list
# Start/stop/restart
slinitctl start myservice # start and mark active
slinitctl start --pin myservice # start and pin in started state
slinitctl wake myservice # start without marking active
slinitctl stop myservice # stop
slinitctl stop --force myservice # force stop (even with dependents)
slinitctl stop --pin myservice # stop and pin in stopped state
slinitctl release myservice # unmark active (stop if unrequired)
slinitctl restart myservice
slinitctl restart --force myservice # force restart
slinitctl unpin myservice # remove start/stop pins
# Service status
slinitctl status myservice
slinitctl is-started myservice # exit 0 if started, 1 otherwise
slinitctl is-failed myservice # exit 0 if failed, 1 otherwise
# Trigger / untrigger
slinitctl trigger mytrigger
slinitctl untrigger mytrigger # reset trigger state
# Send signal to a service process
slinitctl signal HUP myservice
# Pause/continue (SIGSTOP/SIGCONT)
slinitctl pause myservice
slinitctl continue myservice
# Start once (no auto-restart)
slinitctl once myservice
# View buffered service output
slinitctl catlog myservice
slinitctl catlog --clear myservice
# Attach to service virtual TTY (screen-like, Ctrl+] to detach)
slinitctl attach myservice
# Reload config from disk (without restart)
slinitctl reload myservice
# Unload a stopped service from memory
slinitctl unload myservice
# Runtime environment management
slinitctl setenv myservice KEY=VALUE
slinitctl unsetenv myservice KEY
slinitctl getallenv myservice
# Runtime dependency management
slinitctl add-dep myservice depends-on otherservice
slinitctl rm-dep myservice waits-for otherservice
# Enable/disable (add/remove waits-for dep on boot or enable-via service)
slinitctl enable myservice
slinitctl enable --from mygroup myservice # enable from a specific service
slinitctl disable myservice
# Offline enable/disable (without daemon, operates on waits-for.d symlinks)
slinitctl --offline enable myservice
slinitctl --offline --services-dir /etc/slinit.d disable myservice
slinitctl --offline --from mygroup enable myservice
# Query service dependents
slinitctl dependents myservice
# Query loader mechanism
slinitctl query-load-mech
# Boot timing analysis
slinitctl boot-time
# Initiate system shutdown
slinitctl shutdown poweroff
slinitctl shutdown reboot
slinitctl shutdown halt
slinitctl shutdown softreboot # restart slinit without kernel reboot
# Scheduled shutdown + cancel
slinitctl shutdown reboot +5 # in 5 minutes
slinitctl shutdown poweroff 18:30 # at 18:30 today/tomorrow
slinitctl shutdown -c # cancel a pending shutdown
slinitctl shutdown --status # report pending shutdown, if any
# Dependency inspection
slinitctl graph myservice # dep graph rooted at myservice
slinitctl analyze # global dep-graph overview
# Connect to system/user instance explicitly
slinitctl --system list
slinitctl --user list
slinitctl -p /tmp/test.socket list # custom socket pathConfiguration linter. Validates service files offline or using a running daemon's context:
# Offline mode (default)
slinit-check -d /etc/slinit.d myservice
# Online mode (queries running daemon for service dirs and env)
slinit-check --online myservice
slinit-check --online -p /run/slinit.ctl myserviceChecks: file existence, type validity, command executability, dependency references, circular dependencies, depth limits.
Event watcher that subscribes to service state changes and optionally executes commands:
# Watch all events
slinit-monitor
# Execute command on state change (%n=name, %s=state, %v=event)
slinit-monitor -c 'echo "Service %n changed to %s (event: %v)"'Generates a bootable service-description directory skeleton — top-level
boot target, optional system-mounts + network stubs, N agetty
services (with correct inittab-id), env-file with HOSTNAME/TZ/PATH,
optional shutdown-hook sample, README. Inspired by
s6-linux-init-maker.
# Default layout to /etc/slinit/boot.d
slinit-init-maker
# Custom layout: 4 ttys, specific hostname/tz, no network stub
slinit-init-maker --ttys 4 --hostname myhost --tz Europe/Bucharest \
--output /tmp/boot.d --with-shutdown-hook
# Preview without touching the disk
slinit-init-maker --dry-runEmergency userspace cleanup: kill(-1, SIGTERM) → grace period →
kill(-1, SIGKILL). Intended for recovery scenarios where the orderly
shutdown path is unavailable — not a replacement for
slinitctl shutdown.
slinit-nuke # TERM, wait 2s, KILL
slinit-nuke --grace 500ms
slinit-nuke -9 # skip TERM, SIGKILL immediatelyStandalone shutdown utility. Can talk to a running slinit or — with
--system — perform the shutdown sequence directly. Invocable as
slinit-reboot, slinit-halt, or slinit-soft-reboot via symlinks.
slinit-shutdown -r # reboot
slinit-shutdown -p # poweroff
slinit-shutdown -h # halt
slinit-shutdown -s # soft reboot
slinit-shutdown -k # kexecThin argv-translating shims over slinitctl for admins used to
OpenRC. They exec slinitctl (resolved via $PATH or the SLINITCTL
env var), so output, exit codes and flag precedence mirror
slinitctl's own.
# rc-service — service control
rc-service nginx start
rc-service nginx stop
rc-service nginx status
rc-service --exists nginx # → slinitctl is-started nginx
rc-service --list # → slinitctl list
# rc-update — runlevel membership (modelled as runlevel-<name> services)
rc-update add nginx default # → slinitctl --from runlevel-default enable nginx
rc-update del nginx boot # → slinitctl --from runlevel-boot disable nginx
rc-update show # → slinitctl graph runlevel-default
rc-update update # no-op (slinit has no dep cache)
# rc-status — status listing
rc-status # → slinitctl list
rc-status default # → slinitctl graph runlevel-default
rc-status --list # list known OpenRC runlevel names
rc-status --runlevel # print "default" (slinit has no current runlevel)/etc/rc.conf and /etc/conf.d/<name> are sourced automatically
before every init.d script action (see Project structure notes),
so OpenRC per-service config files like /etc/conf.d/nginx keep
working unchanged.
slinit follows Go-idiomatic patterns while preserving dinit's proven service management design:
- Goroutines + channels replace dinit's dasynq event loop
- Interface + struct embedding replaces C++ virtual method dispatch
- Two-phase state transitions (propagation + execution) preserve correctness from dinit
- One goroutine per child process for monitoring, with channel-based notification
- Binary control protocol (v6) over Unix domain sockets, goroutine-per-connection
- Push notifications: SERVICEEVENT5/ENVEVENT for real-time tracking
- PID 1 shutdown sequence: shutdown hooks, process cleanup, filesystem sync, reboot syscalls
| Signal | Action | Source |
|---|---|---|
SIGTERM |
reboot | busybox reboot |
SIGINT |
reboot | Ctrl-Alt-Del (via CAD) |
SIGQUIT |
poweroff | -- |
SIGUSR1 |
reopen control socket | recovery when fs writable |
SIGUSR2 |
poweroff | busybox poweroff |
SIGHUP |
ignored | -- |
SIGCHLD |
reap orphans | child process exit |
SIGRTMIN+3 |
halt | systemd-compat container |
SIGRTMIN+4 |
poweroff | systemd-compat container |
SIGRTMIN+5 |
reboot | systemd-compat container |
SIGRTMIN+6 |
kexec | systemd-compat container |
RT signals let kill -s RTMIN+4 1 from inside a container trigger a
clean shutdown without needing slinitctl present in the image.
Signal-driven shutdown (SIGTERM/SIGINT/SIGQUIT/SIGUSR2/SIGRTMIN+3..+6)
can be gated by /etc/slinit/shutdown.allow — see Features
above. The gate applies only to the initial trigger; a second press
of Ctrl+Alt+Del or a repeated RT signal always escalates.
slinit/
├── cmd/
│ ├── slinit/ # Daemon entry point (incl. SysV argv[0] dispatch)
│ ├── slinitctl/ # Control CLI (36 commands, incl. attach)
│ ├── slinit-check/ # Config linter (offline + online)
│ ├── slinit-monitor/ # Event watcher + command executor
│ ├── slinit-shutdown/ # Standalone shutdown utility (+ reboot/halt/soft symlinks)
│ ├── slinit-init-maker/ # Bootable service-dir generator (s6-linux-init-maker inspired)
│ ├── slinit-nuke/ # Emergency kill-all (TERM → grace → KILL)
│ ├── slinit-mount/ # Autofs lazy-mount helper
│ ├── slinit-checkpath/ # Path-validation helper
│ ├── rc-service/ # OpenRC compat: thin shim over slinitctl
│ ├── rc-update/ # OpenRC compat: runlevel membership via runlevel-<name> services
│ └── rc-status/ # OpenRC compat: status listing
├── pkg/
│ ├── service/ # Service types, state machine, dependency graph
│ ├── config/ # Dinit-compatible config parser + loader, init.d/LSB, OpenRC conf.d wrapper
│ ├── control/ # Control socket protocol (v6) and server
│ ├── shutdown/ # PID 1 init, shutdown executor, soft-reboot, clock guard, run-mode
│ ├── process/ # Process execution, monitoring, attrs, caps
│ ├── eventloop/ # Event loop, signals, timers
│ ├── logging/ # Console logger (wallclock / ISO / TAI64N / none)
│ ├── utmp/ # UTMPX cgo wrapper (boot + logout + shutdown records)
│ ├── autofs/ # Autofs direct-mount helper
│ ├── checkpath/ # Path permission / ownership verifier
│ └── platform/ # Container & VM auto-detect (docker/lxc/podman/wsl/xen/kvm/qemu/vmware/hyperv/vbox/bochs)
├── internal/util/ # Path and parsing utilities
├── completions/ # Shell completions (bash, zsh, fish)
├── demo/ # QEMU demo environment
├── tests/functional/ # 52 QEMU-based integration tests
├── tests/fuzz/ # 21 fuzz targets (config, protocol, autofs, process parsers)
└── tests/performance/ # Performance and stress harness
# Unit tests (~751 tests + benchmarks across 21 packages)
go test ./...
# Functional tests (52 QEMU-based integration tests)
./tests/functional/run-tests.sh
# Fuzz targets (18 targets across 4 files)
go test -fuzz=FuzzParseConfig ./tests/fuzz- Phase 1: Foundation -- types, state machine, config parser, event loop
- Phase 2: Process services -- fork/exec, child monitoring, restart logic
- Phase 3: Full dependency graph -- all 6 dep types, TriggeredService, BGProcessService
- Phase 4: Control protocol +
slinitctlCLI - Phase 5: PID 1 mode + shutdown sequence
- Phase 6: Advanced features -- catlog, reload, ready-notification, socket activation, provides, unload, consumer-of, logfile, wake/release, is-started/is-failed, setenv/getallenv, add-dep/rm-dep, enable/disable, nice/oom/ioprio/cgroup/rlimits, capabilities/securebits, unmask-intr, auto-recovery, starts-rwfs/starts-log, pass-cs-fd, kexec
- Phase 7: Container mode, env substitution, shutdown hooks, UTMPX
- Phase 8: CLI flags batch -- unpin, softreboot, --system/-s/--user/-u, --no-wait/--pin/--force, --ignore-unstarted, --offline/-o/--services-dir/-d, --use-passed-cfd/--from, multiple default service dirs
- Phase 9: Daemon flags -- --system-mgr, --env-file, --ready-fd, --log-file, --cgroup-path, SLINIT_SERVICENAME/SLINIT_SERVICEDSCDIR, advanced env substitution, command +=, load-options, kernel cmdline filtering
- Phase 10: Push notifications -- SERVICEEVENT, LISTENENV/ENVEVENT, mutex-serialized writes
- Phase 11: Protocol v5 -- LISTSERVICES5/SERVICESTATUS5/SERVICEEVENT5, slinit-check, slinit-monitor, @include/@include-opt
- Phase 12: Complete dinit parity -- @meta, env-file meta-commands, PINNEDSTOPPED/PINNEDSTARTED, SERVICE_DESC_ERR/SERVICE_LOAD_ERR, PREACK, QUERY_LOAD_MECH, DEPENDENTS, $/NAME word-splitting, service templates (name@arg), @meta enable-via, SIGUSR1 socket reopen, soft-reboot shutdown hooks
- Phase 13: Runit-inspired features -- finish-command, ready-check-command, pre-stop-hook, env-dir, control-command (custom signal handlers), chroot, new-session, lock-file, close-fds, pause/continue, log rotation (size/time/max-files), log filtering (include/exclude regex), log processor, down-file marker, once command
- Phase 14: /etc/init.d auto-detect with LSB header parsing, BSD rc.d support
- Phase 15: Shutdown info display, escalating force shutdown, cron-like periodic tasks, soft parallel start limit, proper socket activation (multiple sockets, TCP/UDP, on-demand)
- Phase 16: Multi-service shared logger (SharedLogMux -- N producers → single logger, line-prefixed)
- Phase 17: Virtual TTY -- screen-like attach/detach via PTY allocation, ring buffer scrollback,
slinitctl attach - Phase 18: s6-linux-init parity -- catch-all logger, TAI64N/ISO/none timestamps, scheduled shutdown + cancel + status, wall broadcasts,
/etc/shutdown.allowaccess control, configurable grace, global rlimits, RT-signal container shutdown (SIGRTMIN+3..+6), UTMPX logout + wtmp RUN_LVL shutdown, kernel cmdline snapshot,/runtmpfs run-mode, configurable devtmpfs, SysV argv[0] compat (halt/poweroff/reboot),slinit-init-maker,slinit-nuke - Phase 19: OpenRC UX compat --
rc-service/rc-update/rc-statusargv shims,/etc/rc.conf+/etc/conf.d/<name>sourcing viash -cwrapper, runlevels modelled asrunlevel-<name>services, named-runlevel dispatch (init default|single|nonetwork|boot|sysinit)