Switch Root¶
Finit supports switching from an initramfs to a real root filesystem
using the initctl switch-root command. This is useful for systems
that use an initramfs for early boot (LUKS, LVM, network boot, etc.)
and need to transition to the real root before starting services.
Usage¶
NEWROOT: Path to the mounted new root filesystem (e.g.,/mnt/root)INIT: Optional path to init on the new root (default:/sbin/init)
Requirements¶
- Must be run during runlevel S (bootstrap) or runlevel 1
NEWROOTmust be a mount point (different device than /)INITmust exist and be executable on the new root- Finit must be running as PID 1 (in initramfs)
How It Works¶
- Runs
HOOK_SWITCH_ROOTfor any cleanup scripts/plugins - Runs
HOOK_SHUTDOWNto notify plugins - Stops all services and kills remaining processes
- Exits all plugins gracefully
- Moves
/dev,/proc,/sys,/runto new root - Deletes initramfs contents (if on tmpfs/ramfs) to free memory
- Moves new root mount to
/ - Chroots to new root
- Reopens
/dev/consolefor stdin/stdout/stderr - Execs new init as PID 1
Example: Initramfs finit.conf¶
Configuration file /etc/finit.conf in the initramfs:
# /etc/finit.conf in initramfs
# Mount the real root filesystem
run mount-root {
description = "Mounting root filesystem"
runlevel = "S"
command = "/bin/mount /dev/sda1 /mnt/root"
}
# Switch to real root after mount completes
run switch-root {
description = "Switching to real root"
runlevel = "S"
command = "/sbin/initctl switch-root /mnt/root"
}
For more complex setups (LUKS, LVM, etc.):
# Unlock LUKS volume
# The tty setting is required so cryptsetup can prompt for a passphrase
run cryptsetup {
description = "Unlocking encrypted root"
runlevel = "S"
tty = "@console"
command = "/sbin/cryptsetup open /dev/sda2 cryptroot"
}
# Activate LVM
run lvm {
description = "Activating LVM volumes"
runlevel = "S"
command = "/sbin/lvm vgchange -ay"
}
# Mount root
run mount-root {
description = "Mounting root"
runlevel = "S"
command = "/bin/mount /dev/vg0/root /mnt/root"
}
# Switch root
run switch-root {
description = "Switching to real root"
runlevel = "S"
command = "/sbin/initctl switch-root /mnt/root"
}
Example: Using Runlevel 1 for Switch Root¶
For more complex initramfs setups where ordering of tasks becomes difficult in runlevel S, you can perform the switch-root in runlevel 1:
# /etc/finit.conf in initramfs
# Start mdevd for device handling
service mdevd {
description = "Device event daemon"
runlevel = "S"
notify = "s6"
command = "/sbin/mdevd -D %n"
}
run coldplug {
description = "Coldplug devices"
runlevel = "S"
conditions = { "service/mdevd/ready" }
command = "/sbin/mdevd-coldplug"
}
# Mount the real root filesystem (after devices are ready)
run mount-root {
description = "Mounting root"
runlevel = "S"
conditions = { "run/coldplug/success" }
command = "/bin/mount /dev/sda1 /mnt/root"
}
# Transition to runlevel 1 after all S tasks complete
# The switch-root runs cleanly in runlevel 1
run switch-root {
description = "Switching to real root"
runlevel = "1"
command = "/sbin/initctl switch-root /mnt/root"
}
This approach separates the initramfs setup (runlevel S) from the switch-root operation (runlevel 1), making task ordering simpler.
Hooks¶
The HOOK_SWITCH_ROOT hook runs before the switch begins. Use it for:
- Saving state to the new root
- Unmounting initramfs-only mounts
- Cleanup tasks
Plugins can register for HOOK_SWITCH_ROOT just like other hooks:
static void my_switch_root_hook(void *arg)
{
/* Cleanup before switch_root */
}
static plugin_t plugin = {
.name = "my-plugin",
.hook[HOOK_SWITCH_ROOT] = {
.cb = my_switch_root_hook
}
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
Conditions¶
After switch_root, the new finit instance starts fresh. No conditions or state are preserved across the switch. The new finit will:
- Re-read
/etc/finit.conffrom the new root - Re-initialize all conditions
- Start services according to the new configuration
See Also¶
- switch_root(8) - util-linux switch_root utility
- Kernel initramfs documentation