1

I have a periodic task that runs on a cloud VM. The machine type is quite expensive, so I have a systemd unit service that launches on boot, runs a script and then shuts the machine off to save money. This works as designed.

I've had trouble doing maintenance to the machine because if I login and issue a service [] stop command, of course the system shuts down immediately (as it's supposed to).

I think I should be able to use OnFailure for this, but I'd also like the system to shutdown if the script fails for some reason (rather than leave the VM spinning in the event of a crash). Or some use of ExecStop?

There would be three cases:

  • Process executes and terminates properly (e.g. exit code 0), system power off
  • Process crashes (e.g. exit code != 0), system power off
  • Service terminated/paused by user? system stays on

Simple solution is to give the service a grace period of a minute before starting, login, disable, reboot (because the service has already started), perform maintenance tasks and then re-enable. But this seems a bit clunky. The service definition is below, for reference.

[Unit]
Description="Run job"
After=network.target
StartLimitInterval=200
StartLimitBurst=30
OnFailure=systemd-poweroff.service

[Service]
Type=simple
Restart=on-failure
RestartSec=30
ExecStartPre=/bin/sleep 60
ExecStart=/my/script.sh
ExecStopPost=sudo systemctl poweroff -i
User=foo

[Install]
WantedBy=multi-user.target

Just a note the script is run as a normal user due to the environment setup rather than anything security conscious, hence the sudo here (the account is specifically given passwordless sudo rights for poweroff).

1 Answer 1

0

Instead of running poweroff in an ExecStopPost action, move it to a separate service. That is, make your "Run job" service look something like this:

[Unit]
Description="Run job"
After=network.target
Requires=my-poweroff.service
Before=my-poweroff.service

[Service]
Type=oneshot
Restart=on-failure
RestartSec=30
ExecStart=/my/script.sh
User=foo

[Install]
WantedBy=multi-user.target

Note that we're using Type=oneshot instead of Type=simple, because this way the service isn't "started" until the script completes. This is important because we're triggering the poweroff through a Requires=/Before= dependency; using Type=simple, this job and the poweroff job would start at the same time. Using Type=oneshot means that the poweroff job won't run until this service has exited.

Then in my-poweroff.service, you have:

[Unit]
ConditionPathExists=!/run/nopoweroff

[Service]
Type=simple
ExecStart=/usr/bin/systemctl poweroff -i

Now, if you want to prevent the poweroff, just touch /run/nopoweroff before your "Run job" service has finished.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .