1. Overview
When we boot up our system, we might often find ourselves needing to perform repetitive tasks. Some examples include starting background tasks, setting environment variables, and updating package repositories on the system. For most of these tasks, Linux provides various mechanisms for running scripts on the system boot-up event.
However, there are times when we’ll need to run scripts after the system has established an internet connection. The typical mechanisms might not work because the network connectivity is not instantly ready after it starts up.
In this tutorial, we’ll look at two different methods in Linux that allow us to run scripts when the internet connection is available.
2. Problem Statement
A typical repetitive task we usually do is to update the package repositories consistently to keep our package information up to date. Usually, we write a script that runs the apt-get update command and set the script to be triggered automatically on start-up:
$ cat /usr/local/bin/update-repositories-on-bootup
#!/bin/bash
ts=$(date +%s)
sudo apt-get update &> /var/run/update-repositories-on-bootup-output-${ts}.log
The script above is straightforward: we first get the current timestamp and store it in the ts variable. Then, we run the apt-get update command and redirect the stderr and stdout to a log file. Finally, we parameterize the log file with the timestamp we’ve obtained from the first step.
Since the action above requires an active internet connection, it would be good to have a trigger that runs the script after the system is online. In Linux, we can utilize the /etc/network/if-up.d and the SystemD’s After constructs to run scripts after the network connectivity is established. Let’s look at each of the methods in detail.
3. The /etc/network/if-up.d Directory
In Linux, the /etc/network directory contains numerous if-*.d directory that hosts network interface hook scripts. Specifically, any scripts we put into the if-up.d directory will be executed when the network interfaces on the system are brought up.
3.1. Executing a Script Upon Network Interface Activation
Firstly, we place the update-repositories-on-bootup script in the /etc/network/if-up.d directory. To do that, we can use the cp command to copy the script over to the if-up.d directory:
$ sudo cp /usr/local/bin/update-repositories-on-bootup /etc/network/if-up.d/update-repositories-on-bootup
Importantly, we’ll need to set its executable permission bit using the chmod +x command:
$ sudo chmod +x /etc/network/if-up.d/update-repositories-on-bootup
Now, we can reboot the system and verify the execution of our script. Specifically, we can verify the execution by checking for the presence of a log file in the /var/run directory:
$ ls /var/run/update-repositories-on-bootup-output-*
/var/run/update-repositories-on-bootup-output-1700304312.log
/var/run/update-repositories-on-bootup-output-1700304349.log
/var/run/update-repositories-on-bootup-output-1700304361.log
/var/run/update-repositories-on-bootup-output-1700304366.log
Interestingly, we can see that there are four different logs that happens at different timestamp. In other words, the system runs our script four times in a single boot-up. This is due to the fact that the system invokes the scripts in the if-up.d directory for every network interface on the system that’s being brought up. In this case, the system has four different interfaces which cause the update-repositories-on-bootup script to be executed four times as they are being brought up one by one.
3.2. Running the Script for a Specific Interface
If we want to run the scripts only for specific network interfaces, we can check the value of the IFACE environment variable.
The IFACE environment variable will be set to the name of the interface that’s currently being brought up. Therefore, in our script, we can add a condition to only run the command if it matches a desired interface name:
$ cat /etc/network/if-up.d/update-repositories-on-bootup
#!/bin/bash
if [ "$IFACE" != "enp0s3" ]; then
exit 0
fi
ts=$(date +%s)
sudo apt-get update &> /var/run/update-repositories-on-bootup-output-${ts}.log
In the example above, we add the condition to return with exit code 0 if the interface name is not enp0s3. This means we only run the apt-get update command when that particular network interface is officially ready.
3.3. Running the Script Once per Boot
Notably, the system might run the scripts multiple times in a single boot session. This is because the same network interface can be brought up and down multiple times in a single boot for various reasons. This might not be desirable if the task is especially bandwidth-consuming consuming and should only run once after the system boots up.
One trick we can use to ensure that the script only runs once in a single boot session is to utilize a flag file. Concretely, we can create a flag file after we first run the script. Subsequently, the script will skip if it detects the presence of the flag file:
$ cat /etc/network/if-up.d/update-repositories-on-bootup
#!/bin/bash
if test -e /var/run/has_update_repositories_run; then
exit 0
fi
ts=$(date +%s)
sudo apt-get update &> /var/run/update-repositories-on-bootup-output-${ts}.log
touch /var/run/has_update_repositories_run
In our enhancement, we added a condition to skip the execution if the has_update_repositories flag file exists. Otherwise, we’ll run the command as usual and create the flag file /var/run/has_update_repositories_run before we return. On subsequent runs in the same boot session, the presence of the flag file will cause the script to not invoke the main command.
Critically, we put this flag file in the /var/run directory to ensure that it doesn’t survive a reboot. This allows our script to always run on a fresh boot.
4. SystemD Service
In Linux installations with the SystemD init system, we can utilize the power of SystemD service units to fulfill our objective. Specifically, we can define our script as a SystemD service and set network connectivity as a prerequisite for our service.
4.1. Creating a SystemD Service Unit
Firstly, we must create a service unit for our update-repositories-on-bootup script. Concretely, we’ll create an update-repositories-on-bootup.service unit file in the /etc/systemd/system directory.
In the file, we point the ExecStart directive to our update-repositories-on-bootup script. Importantly, we’ll specify that our service is dependent on the network connectivity being up using the After=network-online.target construct:
$ cat /etc/systemd/system/update-repositories-on-bootup.service
[Unit]
Description=Update package repositories on bootup after network is up
After=network-online.target
[Service]
ExecStart=/usr/local/bin/update-repositories-on-bootup
[Install]
WantedBy=multi-user.target
The unit file above defines a SystemD service unit update-repositories-on-bootup. Additionally, the service runs the /usr/local/bin/update-repositories-on-bootup script on starts up. Furthermore, the condition After=network-online.target ensures that the service runs after the system is online.
4.2. Testing the Service Unit
We can test our service unit definition file by starting the service using the systemctl start:
$ sudo systemctl start update-repositories-on-bootup
Then, we can check the status of the service using the systemctl status command:
$ sudo systemctl status update-repositories-on-bootup
● update-repositories-on-bootup.service - Update package repositories on bootup after network is up
Loaded: loaded (/etc/systemd/system/update-repositories-on-bootup.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Sun 2023-11-19 02:05:07 CET; 8s ago
Process: 5860 ExecStart=/usr/local/bin/update-repositories-on-bootup (code=exited, status=0/SUCCESS)
Main PID: 5860 (code=exited, status=0/SUCCESS)
Nov 19 02:05:07 vagrant-VirtualBox systemd[1]: Started Update package repositories on bootup after network is up.
Nov 19 02:05:07 vagrant-VirtualBox systemd[1]: update-repositories-on-bootup.service: Succeeded.
From the output, the first log indicates that the SystemD process starts our service after the network is up. This is due to our After=network-online.target specification that requires the network to be up. Additionally, we can see that our service has exited without any error. This confirms that our service unit file is working perfectly.
4.3. Enabling the Service Unit
Lastly, we’ll need to enable the service using systemctl enable:
$ sudo systemctl enable update-repositories-on-bootup
Created symlink /etc/systemd/system/multi-user.target.wants/update-repositories-on-bootup.service → /etc/systemd/system/update-repositories-on-bootup.service.
By enabling our service, the SystemD daemon will run our update-repositories-on-bootup service on every boot up.
5. Word of Caution
The modern network is highly dynamic and volatile. There are many times when a network interface might be brought down and back up again. For example, a network interface that gets an IP address dynamically from the DHCP protocol might be brought down in order to obtain an IP address.
Therefore, we should not assume that just because our script gets executed on these network connectivity available hooks, the connection will work flawlessly. The better thing to do is to write our script such that it tolerates a minimal amount of network disruption.
6. Conclusion
In this tutorial, we’ve looked at the challenge of running a script when the system is connected to the internet. Then, we’ve explored two solutions. Firstly, we’ve seen how we can place scripts into the /etc/network/if-up.d directory to make the system run them after the network interfaces are brought up. We’ve also looked at several enhancements, such as making the script run for a specific interface and running once per boot session.
Then, we’ve demonstrated a solution for a system that uses the systemd init system. Specifically, we’ve shown that by defining our script as a systemd service unit, we can specify the network-online.target as its dependencies. This ensures that our script is run after the network connectivity is established.