Understanding Runlevel ‘S’ in Debian/Ubuntu: Technical Deep Dive for System Initialization


2 views

While most Linux administrators are familiar with standard runlevels (0-6), Debian-based systems introduce a special runlevel 'S' that behaves differently from traditional single-user mode (runlevel 1). This runlevel serves as the system's initialization phase before transitioning to other runlevels.

In Debian's implementation:

# Typical runlevel S script example
/etc/rcS.d/S01mountkernfs.sh
/etc/rcS.d/S02hostname.sh
/etc/rcS.d/S03checkroot.sh

These scripts execute before any other runlevel-specific scripts, performing critical early system initialization.

Unlike traditional single-user mode (runlevel 1):

  • Runlevel S runs before any other runlevel
  • It's not interactive (no root shell provided)
  • Used for mounting filesystems and other low-level initialization

Services like Firestarter and Shorewall use rcS.d because:

# Example firewall rule in rcS.d
#!/bin/sh
# /etc/rcS.d/S20firewall
iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT

This ensures firewall rules load before networking starts, providing early security.

Consider putting services in rcS.d when they need to:

  • Run before any other services
  • Initialize critical system components
  • Set up security measures early

For most services, traditional runlevel directories (rc2.d, rc3.d, etc.) remain appropriate.

While systemd replaces traditional init systems, it maintains compatibility:

# systemd equivalent for rcS.d functionality
[Unit]
Description=Early firewall rules
DefaultDependencies=no
Before=sysinit.target

The principles remain relevant even in systemd-based systems.


In traditional Unix systems, runlevel 'S' typically refers to Single-User Mode, similar to runlevel 1. However, in Debian and Ubuntu-based systems, this interpretation doesn't quite hold true. Through examining system behavior and package implementations (like Shorewall and Firestarter), we can see runlevel 'S' serves a different purpose.

Debian's init system (both sysvinit and systemd-compat) uses runlevel 'S' as a special transitional state during system initialization. This becomes clear when we examine the boot sequence:

# Typical Debian boot sequence
/etc/init.d/rc S
/etc/init.d/rc 2  # Default runlevel

The key differences from standard implementations:

  • Not a persistent runlevel like traditional 'S'
  • Executes before any other runlevel
  • Used for fundamental system initialization

Packages like Shorewall place their startup scripts in /etc/rcS.d because:

  1. They need to initialize before networking services start
  2. They provide foundational system services

Here's a typical firewall script example that belongs in rcS.d:

#!/bin/sh
### BEGIN INIT INFO
# Provides:          firewall
# Required-Start:    $local_fs
# Required-Stop:     $local_fs
# Default-Start:     S
# Default-Stop:      0 1 6
# Short-Description: Firewall rules
### END INIT INFO

iptables -F
iptables -A INPUT -i lo -j ACCEPT
# ... more rules ...

When deciding where to place your startup script:

Location When to Use
rcS.d System-critical services that must initialize before other runlevels
rc2.d Normal multi-user services (default runlevel)
rc1.d Single-user mode maintenance scripts

To troubleshoot scripts in the S runlevel:

# Check boot messages
dmesg | grep -i rcS

# Manually test script execution
sudo /etc/init.d/rc S

Remember that scripts in rcS.d should be lightweight and avoid dependencies on services that start in normal runlevels.