Writing Systemd Service Units

Some touchpads, typically ELAN ones, aren't perfectly supported yet even in the latest kernel (5.2.8 as I write this). They are much better than before, but not quite there yet. Specifically for ELAN1200 which is used in GL503 series and probably other ASUS laptops from the same generation, this touchpad will lose its mind when resuming from suspend, hence the workaround is to remove its corresponding module (i2c_hid) upon suspend then probe it again on resume. These two tasks can be easily accomplished by the following 2 units (edited from https://wiki.archlinux.org/index.php/Power_management#Suspend/resume_service_files):

$ cat /etc/systemd/system/root-suspend.service 
[Unit]
Description=Local system suspend actions
Before=sleep.target

[Service]
Type=simple
ExecStart=-/usr/bin/modprobe -r -v i2c_hid

[Install]
WantedBy=sleep.target
$ cat /etc/systemd/system/root-resume.service 
[Unit]
Description=Local system resume actions
After=suspend.target

[Service]
Type=simple
ExecStart=-/usr/bin/modprobe -v i2c_hid

[Install]
WantedBy=suspend.target

but that is not enough. The default touchpad configuration is not satisfying to me, I want to enable tapping, don't like the middle mouse emulation in the center of the two click areas (I often ended up triggering middle click whenever I want left or right click) and also want natural scrolling. Due to module reloading above, upon resume the touchpad configuration is restored to default, so I need to do the reconfiguration automatically. After knowing the properties to set via xinput list-props, I made the following script:

$ cat /path/to/reapply-touchpad-config.sh 
#!/bin/bash

sleep 1 # increase if modprobe takes more than 1 second on your machine
declare -x DISPLAY=":0.0"
declare -x XAUTHORITY="/home/myusername/.Xauthority"

touchpadid="ELAN1200:00 04F3:3090 Touchpad"
declare -A configmap=(
  [Device Enabled]="1"
  [libinput Tapping Drag Enabled]="1"
  [libinput Natural Scrolling Enabled]="1"
  [libinput Drag Lock Buttons]=""
  [libinput Tapping Drag Lock Enabled]="0"
  [libinput Accel Speed]="1.000000"
  [libinput Horizontal Scroll Enabled]="1"
  [libinput Disable While Typing Enabled]="1"
  [libinput Left Handed Enabled]="0"
  [libinput Middle Emulation Enabled]="1"
  [libinput Tapping Enabled]="1"
)

for key in "${!configmap[@]}"
do
  xinput set-prop "$touchpadid" "$key" "${configmap[$key]}"
done

and another systemd unit:

$ cat /etc/systemd/system/reapply-touchpad-config.service 
[Unit]
Description=Reapply touchpad configuration upon resume
After=suspend.target

[Service]
Type=simple
User=myusername
ExecStart=/path/to/reapply-touchpad-config.sh

[Install]
WantedBy=suspend.target

roughly similar to root-resume.service above, but executed under my username. Now resume will bring my touchpad back the way I want it.

EDIT:
As of kernel 5.4.30 / 5.5.15, these services are no longer needed and even interfere with the now better built-in driver. Please disable or remove if you experience bad behaving touchpad. With the said kernels, the touchpad now survives sleep/resume cycle.

5 Likes

As always, great stuff @tbg. :grin:

Since I have a functional suspend now I noticed my USB Wifi doesn't wake up when resuming.

Device-2: Ralink RT5372 Wireless Adapter type: USB driver: rt2800usb bus ID: 3-2:6 
chip ID: 148f:5372 IF: wlp9s0f3u2 state: up mac: <filter>

Would I be safe assuming that this script will work for waking it up after suspend?

Ralink rt2800usb WiFi restart service:

Create the service file:

/etc/systemd/system/network-restart.service

With this content:

#/etc/systemd/system/network-restart.service
#sudo systemctl enable network-restart.service
#sudo systemctl start network-restart.service
#sudo systemctl stop network-restart.service
#sudo systemctl disable network-restart.service
#systemctl status network-restart.service
#sudo systemctl daemon-reload

[Unit]
Description=Network Suspend/Resume Service 
Before=sleep.target
StopWhenUnneeded=yes

[Service]
User=root
Type=oneshot
RemainAfterExit=yes
ExecStartPre=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking off'
ExecStart=/usr/bin/sleep 1
ExecStart=-/usr/bin/systemctl stop NetworkManager
ExecStart=/usr/bin/sleep 1
ExecStart=-/usr/bin/ip link set wlp9s0f3u2 down
ExecStart=/usr/bin/sleep 1
ExecStart=-/usr/bin/modprobe -r rt2800usb
ExecStop=/usr/bin/sleep 3
ExecStop=-/usr/bin/modprobe rt2800usb
ExecStop=/usr/bin/sleep 2
ExecStop=-/usr/bin/ip link set wlp9s0f3u2 up
ExecStop=/usr/bin/sleep 2
ExecStop=-/usr/bin/systemctl start NetworkManager
ExecStop=/usr/bin/sleep 2
ExecStop=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking on'
ExecStop=/usr/bin/sleep 1
ExecStop=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli r wifi off'
ExecStop=/usr/bin/sleep 1
ExecStop=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli r wifi on'

[Install]
WantedBy=sleep.target


If your adapter's ID is different than “wlp9s0f3u2” you will need to substitute you own adapter’s ID into the service file. If you are using a different driver module you will also need to substitute it in place of “rt2800usb”.

You can find your adapters driver/module and network device identification with the following command:

inxi -n

The sleep units may be reduced (or eliminated) if you do not like the delay it creates. Be aware though, that doing so may reduce the reliability of the service.

Once you have finished creating, (and saved) the service file, enable the service:

sudo systemctl enable network-restart.service

Then restart.


5 Likes

Worked like a charm.
I have to give it a few seconds after coming back from suspend but that's fine with me.
I usually hit the keyboard and walk off to start coffee.

Thank you very much! :grin:

3 Likes

You're very welcome, glad that did the trick. :smile:

3 Likes

This service should resolve suspend issues with Bluetooth. The service disables Wifi and Bluetooth on suspend, then re-enables both upon resume.


Bluetooth Suspend Service


With a text editor create:

/etc/systemd/system/bt-restart.service

bt restart service file contents:

#/etc/systemd/system/bt-restart.service
#sudo systemctl enable bt-restart.service
#sudo systemctl start bt-restart.service
#sudo systemctl stop bt-restart.service
#sudo systemctl disable bt-restart.service
#systemctl status bt-restart.service
#sudo systemctl daemon-reload

[Unit]
Description=Disable, then restart bt
Before=sleep.target
StopWhenUnneeded=yes

[Service]
User=root
Type=oneshot
RemainAfterExit=yes
ExecStartPre=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking off'
ExecStart=/usr/bin/sleep 1
ExecStart=/usr/bin/sudo -E  /usr/local/bin/disable_bt.sh
ExecStop=/usr/bin/sudo -E  /usr/local/bin/enable_bt.sh
ExecStop=/usr/bin/sleep 2
ExecStopPost=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking on'

[Install]
WantedBy=sleep.target


Disable BT Suspend Script


Create the script that is to disable BT before suspending.

With a text editor create:

/usr/local/bin/disable_bt.sh

Suspend script contents:

#!/bin/bash
#Disable BT device
#/usr/local/bin/disable_bt.sh
set -euo pipefail
IFS=$'\n\t'

VENDOR="0489"
PRODUCT="e0a2"

for DIR in $(find /sys/bus/usb/devices/ -maxdepth 1 -type l); do
  if [[ -f $DIR/idVendor && -f $DIR/idProduct &&
        $(cat $DIR/idVendor) == $VENDOR && $(cat $DIR/idProduct) == $PRODUCT ]]; then
    echo 0 > $DIR/authorized
  fi
done

Save the file, and exit the text editor.


Enable BT Resume Script


Create the script that is to be executed after coming out of suspend.

With a text editor create:

/usr/local/bin/enable_bt.sh

Resume script contents:

#!/bin/bash
#Enable BT device
#/usr/local/bin/enable_bt.sh
set -euo pipefail
IFS=$'\n\t'

VENDOR="0489"
PRODUCT="e0a2"

for DIR in $(find /sys/bus/usb/devices/ -maxdepth 1 -type l); do
  if [[ -f $DIR/idVendor && -f $DIR/idProduct &&
        $(cat $DIR/idVendor) == $VENDOR && $(cat $DIR/idProduct) == $PRODUCT ]]; then
    echo 1 > $DIR/authorized
  fi
done

Enable the service.

sudo systemctl enable bt-restart.service

Then, ensure both scripts are executable:

chmod +x /usr/local/bin/disable_bt.sh
chmod +x /usr/local/bin/enable_bt.sh

Then restart.


Anyone wishing to disable a different USB device can easily do so by altering the script, inserting your devices ID.

You can find the device ID of of the USB component you wish disabled with the command "lsusb".

Substitute your devices ID you wish disabled in the fields for "VENDOR" and "PRODUCT" in the script above.

These are the fields you must insert your own device ID:

VENDOR="0489"
PRODUCT="e0a2"

4 Likes

I have suspend problem due to bluetooth either not fast enough or not willing to suspend, hopefully this will solve that problem.

Give it a go. Hope it works for you. Usually I always test my services before posting them, but I do not use bluetooth. I have it totally disabled on my systems.

It should hopefully work as it is a combination of 2 methods that have been well tested for solving suspend issues.

1 Like

Bluetooth Startup Service

Sometimes network drivers can conflict with each other. In some cases the drivers must be loaded in a specific sequence or they will fail to load properly. This Bluetooth startup service in combination with a Bluetooth blacklist file will ensure that Bluetooth is not started until after the other networking components are initialized.

The following is a simple system service to start your Bluetooth after your WiFi at system startup.

The best way to ensure there is no driver conflict with WiFi or Ethernet at startup is to blacklist the btusb module.

Run this command to prevent Bluetooth from initiating early in the startup sequence:

echo 'blacklist btusb' | sudo tee /etc/modprobe.d/blacklist_btusb.conf

This will create /etc/modprobe.d/blacklist_btusb.conf which will prevent the bluetooth module from automatic early startup.


Then create the following service which will restart the Bluetooth components automatically after the other network components:


Restart Bluetooth Service

With a text editor create:

/etc/systemd/system/restart-bt.service

Service file contents:

#/etc/systemd/system/restart-bt.service
#systemctl enable restart-bt.service
#systemctl start restart-bt.service
#systemctl stop restart-bt.service
#systemctl disable restart-bt.service
#systemctl status restart-bt.service
#systemctl daemon-reload

[Unit]
Description=Restart Bluetooth Service
WantedBy=multi-user.target 
After=network.target
Wants=bluetooth.target
StopWhenUnneeded=yes

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/usr/bin/sleep 3
ExecStart=/usr/bin/modprobe btusb
ExecStop=/usr/bin/systemctl restart bluetooth

[Install]
WantedBy=multi-user.target 

Save the newly created service file, then enable the service:

systemctl enable restart-bt.service

Then restart.

Bluetooth should now be automatically started after your other network components have been loaded at startup.


4 Likes

Automatically Restart Dropped WiFi Connections


Here is a revised service for those who have issues with their WiFi routinely disconnecting. For those who have tried everything to fix their connection drops without success this service may help.

It is always preferable to fix your connectivity issues by changing driver options, firmware, kernel version, etc, etc. Unfortunately some adapters still have disconnection issues regardless of the fixes you apply. This is a massive annoyance if your adapter disconnects regularly. This service will hopefully alleviate that inconvenience somewhat by automatically restarting your network connection for you.

Some users are unlucky enough to have to reboot every time their WiFi goes down. This service should restart your networking components without requiring a reboot. Even if you don't need to reboot, you will find this service far more convenient than manually restarting your connection.

This service pings Google's servers to determine if your connection is up. If your connection is down, then the service will automatically initiate a restart of all network components. This should revive your connection without any need for manual intervention. Sometimes a recurring ping as used in this service also helps to keep a shaky connection alive.

This service should automatically restart your network connection shortly after it drops. From the time when it detects a loss of connectivity the script can be as quick as 5 seconds in re-initiating a connection.

To get this network auto restart functionality you must create one service file and one associated script.


Network Restart Service:


With a text editor create the systemd service file:

/etc/systemd/system/network-restart.service

With the following contents:

# to terminate running service & script:  sudo systemctl stop network-restart.service && sudo killall -9 network_restart
#/etc/systemd/system/network-restart.service
#sudo systemctl enable network-restart.service
#sudo systemctl start network-restart.service
#sudo systemctl stop network-restart.service
#sudo systemctl disable network-restart.service
#systemctl status network-restart.service
#sudo systemctl daemon-reload

[Unit]
Description=Network Restart Service 
WantedBy=network.target 

[Service]
User=root
Type=simple
Restart=always
RestartSec=3
RemainAfterExit=yes
Restart=on-failure
StartLimitIntervalSec=0
ExecStartPre=-sleep 15
ExecStart=/usr/local/sbin/network_restart.sh

[Install]
WantedBy=network.target

Once you have created and saved the service file, enable the service:

sudo systemctl enable network-restart.service

Network Restart Script:


With a text editor create:

/usr/local/sbin/network_restart.sh

network_restart.sh script contents:

#!/bin/bash
# to terminate running service & script:  sudo systemctl stop network-restart.service && sudo killall -9 network_restart
#/usr/local/sbin/network_restart.sh
while true; do
     ping -c 1 8.8.8.8  | grep received
     if [ $? -eq 0 ]; then sleep 2
else
  echo "Connection broken, restarting network connection"
     /bin/sh -c 'nmcli networking off'
       systemctl stop NetworkManager
        ip link set wlp3s0 down
         modprobe -r rtl8723be
           sleep 1
            modprobe rtl8723be
             sleep .5
             ip link set wlp3s0 up
             sleep .5
            systemctl start NetworkManager
           /bin/sh -c 'nmcli networking on'
          /bin/sh -c 'nmcli r wifi off'
         sleep .5
       /bin/sh -c 'nmcli r wifi on'
      sleep 10
     break
   fi
 done
exec "$ScriptLoc"/usr/local/sbin/network_restart.sh && exit

Make the script executable:

sudo chmod +x /usr/local/sbin/network_restart.sh

The group of numbers "8.8.8.8" is Googles server address. If you would prefer to ping another server such as Cloudflare then change the address to "1.1.1.1" instead.

There are several lines in the script that will need to be modified if you have different networking components. You can find the required information by running the command "inxi -n". You must replace "wlp3s0" with your adapters designation returned from the "inxi -n" command if your identification is different.

ip link set wlp3s0 down
ip link set wlp3s0 up

You must also insert your driver module in place of "rtl8723be" in the script if your adapter uses a different module. Modify these lines (as below) in the script to reflect the driver module your adapter uses:

modprobe -r rtl8723be
modprobe rtl8723be

Drivers such as the Atheros ath10k or Intel iwlwifi drivers use several modules rather than only one. If your adapter uses several drivers the script will need to be modified to list both of the modules it uses.

After creating the service file and the associated script reboot the computer.

After rebooting you can use the following command to test how well the connection is reestablished after it is broken:

nmcli networking off

This service and script are designed to be self-respawning. This makes the service and script available to continuously refresh your internet connection. It also makes it very difficult to interrupt the script temporarily. To terminate the running service and script you must issue this command:

sudo systemctl stop network-restart.service && sudo killall -9 network_restart 

I hope this helps some people with flakey adapters that drop the connection on a regular basis. While this is only a workaround hopefully it will make your adapter more usable until a permanent fix is found.


Note:

If you use suspend or hibernate this service could possibly interfere with proper suspension. However, tests on my system turned up no negative side effects from the service.


Please leave feedback if the script works well for you, (or even if it doesn't. It may simply require a little tweaking to get it to work properly on your system. Hopefully I can assist you to get it working if need be.


9 Likes

My apologies, as this is a repost. I had posted this earlier on this thread together with similar versions. As that was confusing for some, I am now separating them into different posts for clarities sake when I link to it.

Ath10k WiFi Suspend Service

This ath10k suspend service does not require separate scripts or services. This service is condensed into a single unit, so it is much easier to create.


Combined ath10k network restart service:

Create the following file with a root text editor:

/etc/systemd/system/network-restart.service

Add the following contents to the file:

#/etc/systemd/system/network-restart.service
#sudo systemctl enable network-restart.service
#sudo systemctl start network-restart.service
#sudo systemctl status network-restart.service
#sudo systemctl daemon-reload

[Unit]
Description=Atheros WiFi Suspend Service 
Before=sleep.target
StopWhenUnneeded=yes

[Service]
User=root
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking off'
ExecStart=/usr/bin/sleep 1
ExecStart=/usr/bin/systemctl stop NetworkManager
ExecStart=/usr/bin/ip link set wlp3s0  down
ExecStart=/usr/bin/rmmod  ath10k_pci
ExecStart=/usr/bin/sleep 1
ExecStart=/usr/bin/rmmod  ath10k_core
ExecStop=/usr/bin/sleep 5
ExecStop=/usr/bin/modprobe ath10k_core
ExecStop=/usr/bin/sleep 2
ExecStop=/usr/bin/modprobe ath10k_pci
ExecStop=/usr/bin/sleep 2
ExecStop=/usr/bin/ip link set wlp3s0 up
ExecStop=/usr/bin/sleep 2
ExecStop=/usr/bin/systemctl start NetworkManager
ExecStop=/usr/bin/sleep 1
ExecStop=/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking on'
ExecStop=/usr/bin/sleep 1
ExecStop=/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli r wifi off'
ExecStop=/usr/bin/sleep 1
ExecStop=/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli r wifi on'

[Install]
WantedBy=sleep.target


Once you have created and saved the service file, enable the service:

sudo systemctl enable network-restart.service

Then reboot the computer.


The sleep units in the service may be reduced, (or eliminated) if you do not like the delay it creates. Be aware though, that doing so may reduce the reliability of the service. If the service fails occasionally then this can usually be corrected by increasing the sleep times.

Strangely, some users have reported that the service would work on a specific kernel, but not on another kernel unless the sleep units were increased.


For others wishing to adapt this service to their own setup (if different than above) do the following:

If your adapter's designation is different than “wlp3s0” you will need to substitute your own adapter’s ID into the service file. Your adapter's ID could be "wlan0" or "wlp4s0" or any other designation of a similar format. Any designation starting with "en" or "eth" will be your Ethernet adapter (not your wifi).

If you are using a different driver module(s) you will also need to substitute it in place of “ath10k_pci” and “ath10k_core”.

You can find your adapter driver/module(s) and device ID with the following command:

inxi -n

Substitute your adapter's particulars into the service if your details returned from the inxi command are different from those in the above service.


5 Likes

Today I was trying to get Grav CMS to create backups using a systemd service/timer combo instead of installing cronie (I tried with cronie but the clean cache scripts messed the permissions causing a blank site).

One of the goals was to run it as the http user and a lot of grief later I found that is not possible. Reading documentation - I found that executing a service as a user without a logged in user by the same name is not allowed.

The command execute the backup is a vendor php script which must be executed in the root of the web.

I initially created a script in /usr/bin

#!/bin/bash
cd /srv/http/domain/public
 /usr/bin/php bin/grav backup

But it failed me and I could figure out why. It failed with a cryptic message

cd - file not found /usr/local/bin:/usr/bin

After a lot of frustration and digging I finally found it. I began looking at the existing service definitions and stumbled upon RuntimeDirectory= which accidentally lead me to WorkingDirectory= and the final working thing.

[root@nix system]# cat domain-backup.timer
[Unit]
Description=Run grav scheduler on domain once a day
Unit=domain-backup.service

[Timer]
Unit=domain-backup.service
OnCalendar=daily
AccuracySec=12h
Persistent=true
[root@nix system]# cat domain-backup.service
[Unit]
Description=Run grav backup on domain

[Service]
Type=oneshot
RemainAfterExit=yes
#User=http
WorkingDirectory=/srv/http/domain/public
ExecStart=/usr/bin/php ./bin/grav backup

[Install]
WantedBy=multi-user.target
2 Likes

Thank you so much for posting your solution.

I have rarely had the need to use WorkingDirectory=, but it's good to know. The amount of options with systemd is rather staggering. To figure out the correct options is a bit like a jigsaw puzzle, (but it feels so good when it all comes together).

Congrats, and thanks again for posting.

1 Like

This is a modified version of my Clean-n-sleep service which runs at suspend/resume.

This service version runs at shutdown to clean the system on machines that rarely/never use suspension.

This is a user service (not system) which is placed in the "~/.config/systemd/user/" subdirectory.

This service while simple, does some handy things to clean the system before shutting down.

It helps enhance your privacy by emptying your trash and deleting your browser cache before shutting down.

It also deletes numerous files in ~/.cache including thumbnails that can take up a lot of space if left too long.

The "ExecStartPre" line contains commands to delete items in your trash. I have it set to delete any files in the trash (including external drives) over a day old. You can modify the time trigger in the find commands if you'd like a different time frame.


Clean-N-Pwrdown Service

Create the required service file:

~/.config/systemd/user/clean-n-pwrdown.service

With the following contents:

#service location /home/$USER/.config/systemd/user/clean-n-pwrdown.service
#systemctl --user enable clean-n-pwrdown.service
#systemctl --user start clean-n-pwrdown.service
#systemctl --user status clean-n-pwrdown.service
#systemctl --user stop clean-n-pwrdown.service
#systemctl --user disable clean-n-pwrdown.service
#systemctl --user daemon-reload

[Unit]
Description=Clean system before shutdown
Before=shutdown.target halt.target poweroff.target reboot.target

[Service]
Type=simple
ExecStartPre=/bin/bash -c "find ~/.local/share/Trash/files/ -type f -mtime +1 -exec rm {} \; && find /run/media/$USER/*/.Trash-*/files/ -type f -mtime +1 -exec rm {} \;" 
ExecStart=/bin/bash -c "rm -rf ~/.cache/opera/Cache; rm -rf ~/.cache/falkon/default/Cache; rm -rf ~/.cache/mozilla/firefox/*/cache2/entries; rm -rf ~/.cache/thumbnails/large; rm -rf ~/.cache/thumbnails/normal; mkdir -p ~/.cache/opera/Cache; mkdir -p ~/.cache/falkon/default/Cache; mkdir -p ~/.cache/mozilla/firefox/*/cache2/entries; mkdir -p ~/.cache/thumbnails/large; mkdir -p ~/.cache/thumbnails/normal"

[Install]
WantedBy=shutdown.target halt.target poweroff.target reboot.target


Once you have finished creating, (and saved) the service file, enable the service:

systemctl --user enable --now clean-n-pwrdown.service
systemctl --user daemon-reload

Then restart.


After restarting your trash and browser caches along with your saved thumbnails should now be cleared.

You can alter the locations of my preselected directories if you'd like different directories cleared at shutdown.

If you have any extra items you think would be handy to auto-clean at shutdown please feel free to post your suggestions.


5 Likes

That firefox cache directory is profile specific. To get it to work for any profile and or multiple profiles for the same user use a wildcard:

ExecStart=/bin/bash -c "rm -rf ~/.cache/mozilla/firefox/*/cache2/entries ~/.cache/opera/Cache ..."
2 Likes

Ah yes, of course. I was forgetting the random name generation. Thanks for mentioning that, I will make the edit as you suggested.

3 Likes

Yet another use for systemd units

2 Likes

Awesome tutorial thanks for posting that.

I also have a few examples earlier on this thread on how to automount NFS shares using systemd. I have found using mount/automount units often avoids a start/stop job delay that can happen when using fstab.

1 Like

Systemd service to play a sound on successful boot startup.

Why?
As graphics issues sometimes prevent the GUI login screen from becoming visible, I added a boot sound that tells me my system has successfully booted regardless of the login screen being visible or not:

[Unit]
# Fabby: 2019-12-12: Play boot sound after sound and login service are ready 
Description=Boot Sound
Requires=sound.target
After=systemd-logind.service

[Service]
Type=oneshot
RemainAfterExit=no
# change the following line to point to a sound file that exists on your PC
# /usr/share/sounds/alsa/Noise.wav exists on all Manjaro systems with sound
# Mine is Majel Barett (Star Trek Computer voice) saying "Program loaded and ready"
ExecStart=/usr/bin/aplay /usr/share/sounds/ST_Computer_ProgramLoadedAndReady.wav

[Install]
WantedBy=default.target

on my system this is in /etc/systemd/system/boot_sound.service

4 Likes

This is my first foray into writing a systemd swap unit. So go easy on me if I have any irregularities, and feel free to suggest any alterations. Other than the systemd manpages, information on this type of unit is quite skimpy.

I have used multiple swap files before in the past, but not for a boot drive with an SSD. Generally I might add an extra swap file on platter drives as cheap insurance against a crash because of a memory leak.

I just did an install on an old laptop with a limited amount of ram and a small 60 Gig SSD. I set the drive up quite differently because of the old hardware's limitations. To conserve space I went with a single partition and a swap file equal in size to my RAM. I believe I set the swap file up correctly using fstab to load the swap file at boot. However, I experienced a 90 second stop job at boot using fstab.

Rather than trying to correct my issue with fstab, I figured this was the perfect excuse to write my first swap unit. I usually tweak my virtual memory settings using /etc/sysctl.d/99-sysctl.conf. I thought I'd test managing the virtual memory settings using a service unit rather than using 99-sysctl.conf. I also thought I'd incorporate prioritizing the SSD drives's swap file ahead of any swap files located on another attached platter drive in the service.

A unit configuration file whose name ends in ".swap" encodes information about a swap device or file for memory paging controlled and supervised by systemd. The swap unit is used to load the swap service at boot.


Create The Systemd Swapfile Service:

Create the systemd swapfile service unit file:

/etc/systemd/system/swapfile.service

With the following contents:

[Unit]
Description=Create root swapfile
RequiresMountsFor=/
ConditionPathExists=swapfile

[Service]
Type=oneshot
ExecStart=/usr/bin/fallocate -l 4G /swapfile
ExecStart=/usr/bin/chmod 600 /swapfile
ExecStart=/usr/sbin/mkswap /swapfile
ExecStart=/usr/sbin/swapon /swapfile
ExecStart=/usr/bin/sh -c "sysctl vm.swappiness=10"
ExecStart=/usr/bin/sh -c "sysctl vm.vfs_cache_pressure=50"
RemainAfterExit=true

This service does not need to be enabled as it is launched via the swap unit at boot.

In the service I have my swap file size set to 4 GB. If you would prefer an alternate size such as 2 GB, then alter the line in the service to:

ExecStart=/usr/bin/fallocate -l 2G /swapfile

Create The Systemd Swap Unit:

Create:

/etc/systyemd/system/swapfile.swap

With the following contents:

[Unit]
Description=Enable root swapfile
Requires=swapfile.service
After=swapfile.service

[Swap]
What=/swapfile
Priority=100
TimeoutSec=5

[Install]
WantedBy=multi-user.target

Enable the service:

sudo systemctl enable --now swapfile.swap

To check that the swapfile service is indeed now running enter:

systemctl status -l swapfile --no-pager

To confirm that your swapfile is now enabled and show how much swap is being used, run:

swapon -s

Available swap can also be displayed with:

free -h

To confirm that your swappiness factor is now 10:

cat /proc/sys/vm/swappiness

To confirm that your cache pressure setting is now 50:

cat /proc/sys/vm/vfs_cache_pressure


(Edit:)

I thought I should mention if you are adding a swapfile to a non root partition the naming conventions are different. Here are further swap unit examples from a different drive mounting a secondary swap file. The naming convention uses dashes "-" rather than the standard forward slash "/" used to denote the normal folder hierarchy.

Create the swap unit:

/etc/systemd/system/run-media-htpc-1TB_WD_ext4-swapfile.swap

With the following content:

# cat run-media-htpc-1TB_WD_ext4-swapfile.swap
# sudo systemctl enable --now run-media-htpc-1TB_WD_ext4-swapfile.swap
# sudo systemctl disable --now run-media-htpc-1TB_WD_ext4-swapfile.swap
# systemctl status run-media-htpc-1TB_WD_ext4-swapfile.swap -l --no-pager

[Unit]
Description=Enable swapfile
Requires=swapfile.service
After=swapfile.service

[Swap]
What=/run/media/htpc/1TB_WD_ext4/swapfile
Priority=100
TimeoutSec=5

[Install]
WantedBy=multi-user.target

Create the swap service file:

/etc/systemd/system/swapfile.service
# cat /etc/systemd/system/swapfile.service

[Unit]
Description=Create swapfile
RequiresMountsFor=/run/media/htpc/1TB_WD_ext4
ConditionPathExists=swapfile

[Service]
Type=oneshot
ExecStart=/usr/bin/fallocate -l 4G /run/media/htpc/1TB_WD_ext4/swapfile
ExecStart=/usr/bin/chmod 600 /run/media/htpc/1TB_WD_ext4/swapfile
ExecStart=/usr/sbin/mkswap /run/media/htpc/1TB_WD_ext4/swapfile
ExecStart=/usr/sbin/swapon /run/media/htpc/1TB_WD_ext4/swapfile
ExecStart=/usr/bin/sh -c "sysctl vm.swappiness=10"
ExecStart=/usr/bin/sh -c "sysctl vm.vfs_cache_pressure=50"
RemainAfterExit=true



Edit:

If your swap service stopped working it may be because of a recent bug. The way to get it working again would be to use the dd command rather than fallocate.

See this post for further details:

8 Likes

Forum kindly sponsored by