How to execute a script after a network file system is mounted? How to start a systemd service only after NFS share is mounted and available in Linux? Is it possible to execute a script via systemd service after network share such as NFS, CIFS is mounted in Linux? How to check if a device is mounted with systemd?
I have been getting these questions quite often from different users now. With the introduction of systemd in RHEL/CentOS 7 now we have much more control over how system services, mount points are handled and managed. Although it is possible that the amount of configuration options can be overwhelming even for an advanced users for certain scenarios. Since we have so many options to choose from, we tend to get confuse to choose the best possible option which suffice our requirement.
In this tutorial I will share the systemd dependencies required to check if a network share such as NFS/CIFS is mounted before starting the service.
For the sake of demonstration I will use an NFS share. I have already written a well detailed article on steps to configure NFS server so I will not repeat the same. Let me quickly setup my NFS server with a single share with read write permission.
[root@nfs-server ~]# exportfs -v
/data (sync,wdelay,hide,no_subtree_check,sec=sys,rw,no_root_squash,no_all_squash)
Let's move on to the NFS Client. Since I am using RHEL/CentOS, I will install nfs-utils on the server to access NFS share. For other distributions and more details to mount NFS share you should read: Beginners guide to mount NFS share in Linux with examples
Before I create a systemd unit file, let me try to mount the NFS share manually to make sure the configuration is proper
[root@nfs-client ~]# mount 192.168.43.10:/data /share
[root@nfs-client ~]# df -h /share/
Filesystem Size Used Avail Use% Mounted on
192.168.43.10:/data 17G 2.2G 14G 14% /share
So our share from the NFS server is properly accessible. Let's unmount it as we will mount it using systemd mount unit file.
[root@nfs-client ~]# umount /share/
Below is my systemd unit file for mounting the NFS share
/tmp/share
then the unit name should be temp-share.mount, if your mount point is /var/srv-records/public
then the name of the unit file should be var-srv_records-public.mount
. This is to be inline with how systemd names the mount unit file from /etc/fstab
[root@nfs-client ~]# cat /etc/systemd/system/share.mount [Unit] Description=NFS Share from nfs-server.example.com(192.168.43.10) DefaultDependencies=no Conflicts=umount.target After=network-online.target remote-fs.target Before=umount.target [Mount] What=192.168.43.10:/data Where=/share Type=nfs Options=defaults [Install] WantedBy=multi-user.target
Let's understand the [Unit] section:
- target units will complement all configured dependencies of type
Wants=
orRequires=
with dependencies of typeAfter=
unlessDefaultDependencies=no
is set in the specified units. - We put
umount.target
withConflicts=
to make sure that the conflicting unit is stopped before the other unit is started - Since NFS is dependent on network we put
network-online.target
andremote-fs.target
underAfter=
. - The
netfs
(remote-fs.target
) service is responsible for mounting the network-related file systems listed in/etc/fstab
(NFS, SMBFS/CIFS, NCP, or any line with_netdev
option). - It will only mount these types of filesystems once the system starts the network. If this service is not started at boot time, the network-related file systems will not be mounted.
- Also we would like to call our service before
umount.target
In think the [Mount]
section is pretty much self-explanatory so let's skip the remaining section. If you are still interested to learn about mount unit files then I have written another detailed article on this topic with different examples.
Reload the systemd changes. This is an important step, do not skip this:
[root@nfs-client ~]# systemctl daemon-reload
Let's verify this mount unit service file to make sure it mounts the NFS share
[root@nfs-client ~]# systemctl start share.mount
Check the status of this service
[root@nfs-client ~]# systemctl status share.mount
● share.mount - NFS Share from nfs-server.example.com(192.168.43.10)
Loaded: loaded (/etc/systemd/system/share.mount; enabled-runtime; vendor preset: disabled)
Active: active (mounted) since Sun 2020-08-30 10:40:42 IST; 1s ago
Where: /share
What: 192.168.43.10:/data
Tasks: 0 (limit: 30445)
Memory: 156.0K
CGroup: /system.slice/share.mount
Aug 30 10:40:42 nfs-client.example.com systemd[1]: Mounting NFS Share from nfs-server.example.com(192.168.43.10)...
Aug 30 10:40:42 nfs-client.example.com systemd[1]: Mounted NFS Share from nfs-server.example.com(192.168.43.10).
Verify if the share is mounted on the mount point
[root@nfs-client ~]# df -h /share/
Filesystem Size Used Avail Use% Mounted on
192.168.43.10:/data 17G 2.2G 14G 14% /share
Since everything looks good we will enable the service or else this will not be executed at reboot stage.
[root@nfs-client ~]# systemctl enable share.mount Created symlink /etc/systemd/system/multi-user.target.wants/share.mount → /etc/systemd/system/share.mount.
Post reboot of the node my share is properly mounted:
[root@nfs-client ~]# df -h /share/ Filesystem Size Used Avail Use% Mounted on 192.168.43.10:/data 17G 2.2G 14G 14% /share
/etc/fstab
to mount the NFS share during reboot. The fstab
will also internally create the systemd unit file to mount the NFS share. Go ahead and add below content
# NFS_SERVER:/PATH/TO/EXPORTED/DIR /MOUNT_POINT_ON_CLIENT TYPE_OF_FS OPTIONS DUMP PASS
192.168.43.10:/data /share nfs defaults 0 0
Create script (Optional)
I assume you already have your script which you wish to execute after mounting the NFS share. But for the sake of demonstration I will create a small shell script. I will execute the script as root user but you can also configure it to be called as a certain user instead of root.
This script will perform certain pre-checks and then create a directory under /share
.
#!/bin/bash
NFS="/share"
SCRIPT_NAME=$(basename $0)
log=`echo /tmp/${SCRIPT_NAME%%.*}.log`
echo "Log File Name: $log"
echo "Starting at `date`" | tee -a $log
echo "Check if our share is mounted already"
mount | grep $NFS >> $log
echo "DEBUG: List mount unit files at the stage when script is called during reboot"
systemctl list-units --all --type=mount >> $log
if `mount | grep -q "$NFS"`; then
echo "$NFS found" | tee -a $log
else
echo "$NFS dir not available" | tee -a $log
fi
# start our console log
outdir="$NFS/dir_$(date +%d-%m-%Y-%T)"
echo "Creating directory $outdir" | tee -a $log
mkdir -p $outdir
rc=$?
echo "rc from mkdir: $rc" | tee -a $log
if [[ ! -d "$outdir" ]]; then
echo "Something is wrong, $outdir does not exist" | tee -a $log
exit 7
fi
Create systemd service unit file to execute script after NFS mount
This is main part of this tutorial.
We will cover the dependencies required in the systemd unit file to execute a script after our device is mounted and available.
I prefer to create the service unit file inside /etc/systemd/system
instead of other location
# cat /etc/systemd/system/nfs-share.service [Unit] Description="Execute script after NFS share is mounted" RequiresMountsFor=/share [Service] Type=simple RemainAfterExit=yes ExecStart=/root/create_dir.sh [Install] WantedBy=default.target
We have used RequiresMountsFor
to make sure the provided mount point is in use. This automatically adds dependencies of type Requires=
and After=
for all mount units required to access the specified path.
Let's start the service and verify if it is working
[root@nfs-client ~]# systemctl start nfs-share.service
Check the status of the service
[root@nfs-client ~]# systemctl status nfs-share.service
Now if you check the property of this unit file, it will automatically add Requires=
and After=
section with the provided mount point.
[root@nfs-client ~]# systemctl show nfs-share.service | grep -E 'After=|Requires=' Requires=share.mount sysinit.target -.mount system.slice After=systemd-journald.socket basic.target share.mount sysinit.target -.mount system.slice
So the service has properly started and has already created the directory on the NFS share as expected
[root@nfs-client ~]# ls -l /share/
total 4
drwxr-xr-x 2 root root 4096 Aug 30 11:32 dir_30-08-2020-11:32:19
Next enable the service to make sure it starts automatically after reboot
[root@nfs-client ~]# systemctl enable nfs-share.service
Created symlink /etc/systemd/system/default.target.wants/nfs-share.service → /etc/systemd/system/nfs-share.service.
Next reboot the server and verify if it works:
[root@nfs-client ~]# ls -l /share/ total 8 drwxr-xr-x 2 root root 4096 Aug 30 11:32 dir_30-08-2020-11:32:19 drwxr-xr-x 2 root root 4096 Aug 30 11:36 dir_30-08-2020-11:36:08
So post reboot I have one more directory under the /share
folder so life is good.
This was a very basic example, you may have a script which would require to run for a longer period of time in such case you can use TimeoutStartSec
to define the time period for which the service should wait before killing the script and marking the systemd service as failed.
But be aware that TimeoutStartSec
cannot be used with Type=oneshot
as with this TimeoutStartSec
is disabled by default.
Conclusion
In this tutorial we covered the steps to execute a systemd service after a network device is mounted. This network device can be anything such as NFS, CIFS, SMBFS etc as long as these network shares are mounted locally using systemd the same can be used as a dependency to execute other dependent services. But what if you don't have a control on how these network shares are mounted? We will cover that in our next article.
What's Next
It is possible you don't have a control on how these network shares are mounted? For example a shared folder from Oracle VirtualBox which is mounted by the VirtualBox itself using vboxsf
kernel module. In such case RequiresMountsFor
will not work. Let's see how we can access a network share mounted by VirtualBox shared folder.
How to access VirtualBox shared folder at startup with systemd in Linux