Speeding up OpenRC Startup

This guide contains some tips and tricks to get faster boots with OpenRC.

TL;DR

The Problem

By default OpenRC will start all services in the runlevel before it presents you a login prompt (specifically referring to the getty, I don't use a display manager so I cannot comment on those). This takes up a lot of boot time for no good reason. Do you really need your network related services to be running before you log into your desktop? Couldn't they just start in the background, and be done by the time your bloated web browser finally opens?

I couldn't find any properly documented solution for this problem, so I had to come up with a little hack. The best I could find was this Reddit thread, which describes the exact problem but does not provide a direct solution. It does however mention the use of custom runlevels, which I did end up using.

The Solution

Here's an overview of the hacky solution I came up with:

Let's break it down.

Creating a Custom Runlevel

I called mine async. Name it whatever you want. Run the following as root:

mkdir /etc/runlevels/async

As easy as that.

Moving Stuff Over

Now we will move most of the services from the default runlevel into async. In my case it was safe to move all except for the gettys and local (you may not have gettys there if booting with sysvinit). The easiest approach here is probably to directly move the symlinks from /etc/runlevels/default/ into /etc/runlevels/async/, although you can also use utilities such as rc-update as you normally would. Here's an example (run as root, add or remove services as needed):

cd /etc/runlevels/default
mv net.* sshd chronyd distccd tor libvirtd ../async

Stacking Runlevels

Runlevel stacking is what they call adding a runlevel to another runlevel, i.e. inheriting services from another runlevel. We need this because otherwise the services from the default runlevel would be stopped upon switching to the async runlevel (unless they were also added to it). Run the following as root:

rc-update add -s default async

Starting It Asynchronously on Boot

Note: If you use OpenRC to start the gettys, e.g. you use openrc-init, then it would suffice to boot into the async runlevel by appending softlevel=async to the kernel cmdline. But the getty will get buried by the console logs of the services in async, so you might prefer these next methods that also don't involve messing with the kernel parameters.

I find that the simplest way to achieve this is through a custom script in /etc/local.d/, but for some more complex setups it might be better to write an init script instead (i.e. in /etc/init.d/) and add it to the default runlevel. The local.d approach will work as long as you have the local service enabled in the default runlevel, which at least in Gentoo should be there if you haven't removed it.

Simply create the file /etc/local.d/async.start (or anything in that directory ending in .start) with the following contents:

#!/bin/sh
/sbin/openrc async &

And make it executable: (as root)

chmod +x /etc/local.d/async.start

And you're all set! All the script is doing is switching to the async runlevel, using the '&' character to background the process.

Bonus: Parallel Service Startup

You might already know this, but here is a general tip for faster OpenRC boots: Enable parallel service startup by editing /etc/rc.conf and ensuring that you have an uncommented line with rc_parallel="YES". This will considerably speed up the boot process but won't fix the main issue I've described. However it is safe to use together with the solution that I've demonstrated here.

Conclusion

And just as I was about to post this, I checked again for existing solutions, and guess what I found: NEARLY THE EXACT SAME THING POSTED BY SOMEONE ELSE 3 YEARS AGO! With the same runlevel name and all. I swear on my life it's the first time I'm looking at it.

Seriously though, go check it out if you're using sysvinit, it seems cleaner than my approach. It will not work for openrc-init however, which means my article is still kinda relevant and I'm slightly less mad.