How to enable rc.local shell script on systemd while booting Linux system


We can easily enable rc.local shell script support on systemd while booting the Linux system. Traditionally, the shell script /etc/rc.local used by developers and Linux sysadmin to call other scripts or commands after all services are loaded. Typically /etc/rc.local get called at the end when Linux init switched to a multiuser runlevel. However, by default. /etc/rc.local support is disabled under systemd. This page shows how to enable and execute rc.local shell script during boot using systemd on Linux.

Tutorial details
Difficulty Easy (rss)
Root privileges Yes
Requirements Linux with systemd as init
Time 5m

Enabling rc.local shell script on systemd while booting Linux system

/etc/rc.local compatibility achieved on systemd using special service called rc-local.service. This unit gets called automatically into multi-user.target by systemd-rc-local-generator if /etc/rc.local is executable.

Executing rc.local shell script during boot using systemd

Naturally, create or update/edit the file named /etc/rc.local using your favorite text editor. I am going to use vim command:
$ sudo vim /etc/rc.local
## RHEL/CentOS/Fedora Linux edit the /etc/rc.d/rc.local ##
$ sudo vim /etc/rc.d/rc.local

Append required commands or call script. Here is my file:

#!/bin/sh
# add your commands 
# call your scripts here
 
# let us set stuff for my wifi
/sbin/iw phy0 wowlan enable magic-packet disconnect
 
# last line must be exit 0 
exit 0

Save and close the file when using vim. Make sure you set executable permission using the chmod command:
$ sudo chmod -v +x /etc/rc.local

Turing on rc-local.service on Linux when systemd is init

All we need to do is type the following systemctl command:
$ sudo systemctl enable rc-local.service
Reboot the Linux box:
$ sudo reboot
Verify status after reboot:
$ sudo systemctl status rc-local.service
Here is what we see on screen:

 rc-local.service - /etc/rc.local Compatibility
     Loaded: loaded (/etc/systemd/system/rc-local.service; enabled-runtime; ven>
    Drop-In: /usr/lib/systemd/system/rc-local.service.d
             └─debian.conf
     Active: active (exited) since Wed 2020-11-04 13:29:54 IST; 1h 59min ago
       Docs: man:systemd-rc-local-generator(8)
      Tasks: 0 (limit: 37939)
     Memory: 0B
     CGroup: /system.slice/rc-local.service

Nov 04 13:29:54 nixcraft-wks01 systemd[1]: Starting /etc/rc.local Compatibility
Nov 04 13:29:54 nixcraft-wks01 systemd[1]: Started /etc/rc.local Compatibility.

See how to view status of a service on Linux using systemctl command for more info.

How to see service configuration

Open the terminal app and then type:
$ sudo systemctl cat rc-local.service
We see systemd configuration as follows:

# /etc/systemd/system/rc-local.service
#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.
 
# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.local is executable.
[Unit]
Description=/etc/rc.local Compatibility
Documentation=man:systemd-rc-local-generator(8)
ConditionFileIsExecutable=/etc/rc.local
After=network.target
 
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no
 
# /usr/lib/systemd/system/rc-local.service.d/debian.conf
[Unit]
# not specified by LSB, but has been behaving that way in Debian under SysV
# init and upstart
After=network-online.target
 
# Often contains status messages which users expect to see on the console
# during boot
[Service]
StandardOutput=journal+console
StandardError=journal+console

Note: Run sudo SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/system-generators/systemd-rc-local-generator to debug /etc/rc.local issues when script is not loading.

A note about runlevels

Back in the days when init was the default, we had different runlevels as follows:

  • S – Boot the Linux system
  • 0 – Shutdown the Linux box
  • 6 – Reboot the Linux
  • 1 – Linux Single-user mode used for recovering Linux system in an emergency mode
  • 2 to 5 – Normal operation multiuser system that supported both CLI and GUI and full networking

Often Linux distros and Unix system modified these runlevel values as per their needs. However, /etc/rc.local got called when the system entered multiuser mode via runlevel 2 to 5. However, this default was removed when the majority of Linux distro switched to systemd. Hence, I wrote this quick guide for Linux developers and system administrators.

Creating your own service is easy with systemd on Linux

Instead of shell script in /etc/rc.d/ or call /etc/rc.local we do this now. It only works on Linux and not other Unix variants. Then you just do:

# /etc/systemd/system/my-service-name-goes-here.service
#
# Sample template to call your script or command when systemd boots into multi user mode
#
[Unit]
Before=network.target
[Service]
Type=oneshot
ExecStart=/path/to/command
ExecStart=/path/to/script arg1 arg2
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

For instance here is how we can install wireguard or openvpn iptables rules:

# /etc/systemd/system/wireguard-iptables.service
[Unit]
Before=network.target
[Service]
Type=oneshot
ExecStart=/usr/sbin/iptables -t nat -A POSTROUTING -s 10.8.1.0/24 ! -d 10.8.1.0/24 -j SNAT --to 123.x.x.x
ExecStart=/usr/sbin/iptables -I INPUT -p udp --dport 1194 -j ACCEPT
ExecStart=/usr/sbin/iptables -I FORWARD -s 10.8.1.0/24 -j ACCEPT
ExecStart=/usr/sbin/iptables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=/usr/sbin/iptables -t nat -D POSTROUTING -s 10.8.1.0/24 ! -d 10.8.1.0/24 -j SNAT --to 123.x.x.x
ExecStop=/usr/sbin/iptables -D INPUT -p udp --dport 1194 -j ACCEPT
ExecStop=/usr/sbin/iptables -D FORWARD -s 10.8.1.0/24 -j ACCEPT
ExecStop=/usr/sbin/iptables -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

And then:
$ sudo systemctl enable wireguard-iptables.service
$ sudo systemctl start wireguard-iptables.service
$ sudo systemctl stop wireguard-iptables.service

Conclusion

I hope you find this quick tutorial about enabling /etc/rc.local support useful for backward compatibility and ease of use purposes when using systemd. Make sure you read systemd documentation.

🐧 If you liked this page, please support my work on Patreon or with a donation.
🐧 Get the latest tutorials on SysAdmin, Linux/Unix, Open Source/DevOps topics:
CategoryList of Unix and Linux commands
File Managementcat
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network Utilitiesdig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
3 comments… add one
  • exalib Nov 5, 2020 @ 2:53

    I hate systemd. Old good Debian 8.x was nice. Debian 10 and systemd. I may switch to FreeBSD.

  • rob07 Nov 5, 2020 @ 8:42

    At least on my RHEL/CentOS 8 and Fedora Linux file is /etc/rc.d/rc.local but it is symlinked too:
    ls -l /etc/{,rc.d}/rc.local
    I saw:

    lrwxrwxrwx 1 root root  13 Jul 21 14:57 /etc//rc.local -> rc.d/rc.local
    -rw-r--r-- 1 root root 474 Jul 21 14:57 /etc/rc.d/rc.local
    

    May be update page?

Leave a Reply

Your email address will not be published. Required fields are marked *

Use HTML <pre>...</pre>, <code>...</code> and <kbd>...</kbd> for code samples.