Runlevel at Boot

For whatever reason Ubuntu developers decided to prevent the user from specifying the runlevel from the kernel boot command-line (usually set in the GRUB /boot/grub/menu.lst configuration) or by manually editing the kernel command-line at boot-time.

Ubuntu uses a new init system called  upstart rather than the usual Linux inittab system. To maintain backwards compatibility upstart supports and uses inittab /etc/rc?.d/* init scripts (? is a runlevel from 0-6 or S).

Upstart reads its configuration from the scripts in /etc/event.d/. When the kernel starts the upstart /sbin/init it first reads rc-default which will, if it exists, read the /etc/inittab, look for the default runlevel and adopt it.

Example /etc/inittab setting default runlevel to 3


However, if you're debugging boot issues that prevent the PC booting to a usable log-in prompt, you can't get to /etc/inittab to set the init runlevel and even if you add init 3 to the kernel command line it is ignored by upstart.

The only runlevel that upstart will obey from the kernel command line is single for single-user mode (no GUI, no additional services like apache, etc.). This is translated by upstart to runlevel S and it calls telinit S from /etc/event.d/rc-default.

Emmet Caulfield got annoyed by this issue as much as me and has helpfully  posted a solution in the form of a modified upstart /etc/event.d/rc-default that checks the kernel command line for a command of the form init ? where ? is a runlevel (2-5 or S are safe).

This is the revised /etc/event.d/rc-default:

# rc - runlevel compatibility
# This task guesses what the "default runlevel" should be and starts the
# appropriate script.
# Modified from Ubuntu/gutsy version 2008-02-21 by Emmet Caulfield.
# TJ: bug-fix: removed boot-killing semi-colon from sed expression (;p should be p)
start on stopped rcS

	runlevel --reboot || true

	RL="$(sed -ne 's/.*init \([2-5S]\).*/\1/p' /proc/cmdline || true)"
	if [ -n "$RL" ]; then
	    telinit $RL
	elif [ -r /etc/inittab ]; then
	    RL="$(sed -n -e '/^id:[0-9]*:initdefault:/{s/^id://;s/:.*//;p}' /etc/inittab || true)"
	    if [ -n "$RL" ]; then
		telinit $RL
		telinit 2
	    telinit 2
end script

Now all is needed is to disable Gnome (or KDE) from starting. Unfortunately the standard tool, update-rc.d, won't remove existing links in the /etc/rc?.d/ directories.

$ sudo update-rc.d  gdm start 30 2 . stop 01 0 1 6 .
 System startup links for /etc/init.d/gdm already exist.

Although possible, it's recommended not to manually add/remove the links since if update-rc.d is upgraded its postinst script will cause links to be recreated.

Instead, install sysv-rc-conf, a ncurses text GUI that handles the init services nicely.

sudo apt-get install sysv-rc-conf

Now run it and deselect gdm and any other services you don't want to run for your customised runlevels. Press q to save the changes and quit: sysv-rc-conf