Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
An example with an oneshot service on systemd. #systemd #systemd.service #oneshot

README

Services declared as oneshot are expected to take some action and exit immediatelly (thus, they are not really services, no running processes remain). A common pattern for these type of service is to be defined by a setup and a teardown action.

Let's create a example foo service that when started creates a file, and when stopped it deletes it.

Define setup/teardown actions

Create executable file /opt/foo/setup-foo.sh:

#!/bin/bash
echo "Setting up foo ..."
touch /tmp/foo-activated

Create executable file /opt/foo/teardown-foo.sh:

#!/bin/bash
echo "Tearing down foo ..."
if [ -f /tmp/foo-activated ]; then
    rm /tmp/foo-activated
else
    echo "Doesnt seem to be up: Skipping ..."
fi

Define the service unit

Now, we define the systemd unit file as /etc/systemd/system/foo.service. Note that we must specify RemainAfterExit=true so that systemd considers the service as active after the setup action is successfully finished.

[Unit]
Description=Setup foo
#After=network.target

[Service]
Type=oneshot
ExecStart=/opt/foo/setup-foo.sh
RemainAfterExit=true
ExecStop=/opt/foo/teardown-foo.sh
StandardOutput=journal

[Install]
WantedBy=multi-user.target

Reload the systemd daemon and start the service as normally (systemctl start foo.service). Check the status to verify that the correct actions are taking place.

@rowanj

This comment has been minimized.

Copy link

@rowanj rowanj commented May 10, 2016

Just what I was looking for, thanks!

@jimlynnjulian

This comment has been minimized.

Copy link

@jimlynnjulian jimlynnjulian commented Nov 12, 2016

Don't you have to include "/bin/bash" right after "ExecStart="? Example: ExecStart=/bin/bash /opt/foo/setup-foo.sh

@KostyaEsmukov

This comment has been minimized.

Copy link

@KostyaEsmukov KostyaEsmukov commented Dec 3, 2016

@jimlynnjulian you do, if your setup-foo.sh has no +x bit and a shebang (first #!/bin/bash line).

@galindro

This comment has been minimized.

Copy link

@galindro galindro commented May 19, 2017

@erikbgithub

This comment has been minimized.

Copy link

@erikbgithub erikbgithub commented May 23, 2017

After=network-online.target?

@stoivo

This comment has been minimized.

Copy link

@stoivo stoivo commented Aug 24, 2017

This is the command to reload the systemd daemon systemctl daemon-reload

@ramseyrt

This comment has been minimized.

Copy link

@ramseyrt ramseyrt commented Jan 4, 2018

How do you incorporate this script in a target (assuming I wanted to start multiple scripts as a group)?

I created /etc/systemd/system/foo.target with the following contents:

[Unit]
Description=Foo Application Target
Requires=multi-user.target
Wants=foo.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target
AllowIsolate=yes

I also created the foo.target's directory, /etc/systemd/system/foo.target.wants, and a symbolic link to foo.service in that directory.

With this configuration, I'm able to successfully start the target using systemctl start foo.target but I'm unable to stop it properly (using systemctl stop foo.target). When I stop foo.target systemctl looks like it completes but the /tmp/foo-activated file still exists meaning teardown-foo.sh didn't run.

Thanks in advance!

Rob

@truthadjustr

This comment has been minimized.

Copy link

@truthadjustr truthadjustr commented Feb 20, 2018

very useful.. thank you.

@banan314

This comment has been minimized.

Copy link

@banan314 banan314 commented Aug 26, 2019

You have to enable the service so that it starts on boot:

sudo systemctl enable foo.service

It should result in a message

Created symlink /etc/systemd/system/multi-user.target.wants/foo.service → /etc/systemd/system/foo.service.
@xundeenergie

This comment has been minimized.

Copy link

@xundeenergie xundeenergie commented Feb 5, 2020

Du you have an idea for processes like rsync in a script?
I have a backup-script, where several rsync-processes are startet to sync different home-directories, /opt, /usr/local...

When script starts the first rsync-process, it will fork an systemd stops the whole unit. My backup does not get finished.

Only RemailAfterExit=true can solve the problem in combination with Type=oneshot.
But in combinstion with systemd-timer, which starts the backup-serviice every day, it will not start backup.service on the next day, because the service is active in case of RemainAfterExit=true...

systemd.timer manpage say:

"Note that in case the unit to activate is already active at the time the timer elapses it is not restarted, but simply left running. There is no concept of spawning new service instances in this case. Due to this, services with RemainAfterExit= set (which stay around continuously even after the service's main process exited) are usually not suitable for activation via repetitive timers, as they will only be activated once, and then stay around forever."

@drmalex07

This comment has been minimized.

Copy link
Owner Author

@drmalex07 drmalex07 commented Feb 21, 2020

@xundeenergie
The problem with your setup is that systemd will have no idea when your backup is finished.
A solution would be to define a service of Type=simple (with RemainAfterExit=false) and modify your backup program to stay alive (e.g by waiting on a lock file) until all children processes have finished (successfully or not).
These kinds of services are easily activated by timers.

If the program that performs the backup is out of your control, then you should maybe resort in a plain-old cronjob.

@JamesTheAwesomeDude

This comment has been minimized.

Copy link

@JamesTheAwesomeDude JamesTheAwesomeDude commented Jul 31, 2020

This description was very helpful even for configuring my service which did not require an ExecStop; now I know how to actually write these correctly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.