A few years ago I had a project that needed to run a script after startup with root privilege. It was at Burning Man in 2016 where I had brought my Love Machine Game to detect people falling in love, the machine was for the most part working when I left for the playa but after throwing the commands into a single script I realized my error. In order to allow for Python to have access to the hardware that was doing the galvanic skin response which belonged to a group that the default system user was not part of, hence why it was needing to be ran as root. I approached this issue the same way I try to solve problems when no internet is available by trying to find someone on-site who possessed the requisite knowledge and help me make the script run flawlessly at startup. Sadly nobody was found to help and instead a few times a day I would stop by our booth by the man and make sure that Love Machine was collecting data properly.
After I got back from Burning Man I asked a mentor what is the best approach for running a script at startup on a *nix box. He broke down the different strategies and their appropriate circumstances with the biases of a grey beard which allowed me to get a better feel for the different strategies. At the end of the day the best tool to get a script or command to run at any specific interval is Cron. I am not going to go into the specifics of how it works. Instead I am going to highlight how to make shit run at startup in 3 different ways while outlining the appropriate circumstances of use. I will also be covering how to set up the group/user permissions to allow for these scripts to not have to be ran as root, thusly allowing Cron to do all the work for you with minimal hackery.
Init.d is the directory where the scripts live that need to be ran in the background as daemons until the system is shuts down. The human analogy to how init.d functions is akin to all the non-voluntary functions a human body needs to do in order to keep living; eating, sleeping, breathing, etc.
These system level daemons should be used to keep the system up and running. If it is functionally required to keep the machine going, then it should live as a script in the init.d directory. This is a very popular place to put things that need to run at startup due to the old history behind it and the consistency of these scripts always being ran at startup.
What sucks about this approach is that if someone wants some malicious code to run every time the machine boots then placing it as a copying the file to this directory (which requires root access).
Copy the script to the /etc/init.d/ directory
change the permissions to make it executable by typing: chmod +x /etc/init.d/myStartup.sh
Requires group level permissions to be configured for the user. Check the user group settings via:
Then take the groupName from the second grouping of characters after the read/write permissions
usermod -a -G groupName yourUsername
cp yourFile.sh /etc/init.d
chmod + x /etc/init.d/yourfile.sh
The rc.local script is located at the path /etc/rc.local and is typically used by a system administrator to run any custom services as related to this specific machine. Going back to the human analogy, the rc.local can be thought of as tasks that need to be ran at a certain time in the booting process; in the same way you would not style your hair before washing it. The rc.local is able to be leveraged to systematically load different configurations and scripts as the system boots through its various states. These states are referred to as runlevel changes, hence where the name rc. Unless the task that needs to run at startup has a dependency on the runlevels changing, there is no reason to add it here.
I can’t find a ton of arguments as to why this is a good approach, but if I had to choose between something called init.d and rc.local, I would pick rc.local as I can abstract what it is doing better and the name having local in it leads me to believe I won’t break too much if I make a mistake.
What sucks about this approach is the fact that it is has you referring to a script via a script. This sort of nested abstraction is not of value if it is not dependent on any of the runlevel changes. Plus it does not provide an end to end solution to making shit run as root.
Add the path to your app in the file located at /etc/rc.local
The trailing & allows for the script to be ran as a separate thread/shell
And for sudo access it can be configured like this.
su – USER_FOOBAR -c /PATH/TO/MY_APP &
Before the last exit 0, add the absolute path to your code
Requires group configuration similar to above, but really depends on how kinky the implementation is.
# find the exit 0 at the end of the script then append the rest
Cron is a time-based job scheduler that can be used to run at fixed time, dates, events, or intervals. The origin of the name comes from the greek word for time, chronos. Cron uses a configuration file to determine how often and what it should be doing. When the system is booted cron reads the configuration file and then runs as a background daemon. Individual users have their own cron configuration files that are managed via the crontab application and should not be modified directly.
The biggest limitations I can think of would be if a bad actor changed what the script was doing after the Cron job has been configured. This best way I can think to circumstances vent this would be to perform a check when running the file that ensures the checksum is always the same and then setting that checksum as an environmental variable.
crontab -e @reboot /home/user/test.sh
crontab -u root -e
(crontab -l 2>/dev/null; echo “*/5 * * * * /path/to/job -with args”) | crontab –
To Check the Cron Logs
For 99% of my use cases Cron is the best tool for the job of making scripts run at startup (or really any interval or event) on a *nix box. The fact that it is the most simple tool that does not require muddling with group permissions is why I prefer Cron. While I can admit how confused I was when first trying to just make shit run at startup, the how and why of getting this done has given me a much richer understanding of how to make shit work in Linux, but really I am happy to have gained a deeper understanding of how to get a script to run at startup in the most efficient and elegant manner.