Systemd: How to start/stop services on battery

Is there a way to use systemd to start/stop services when a laptop is on battery or logged in?

As a developer it’s helpful to have things like mongodb and redis autostart, but I would like to suspend them when on battery to save as much as possible.

Asked By: Ivo Anjo

||

I’m not an expert on systemd, but I google better than the average bear. See https://wiki.archlinux.org/index.php/Power_management:

There is just one thing systemd cannot do (as of systemd-204): power management depending on whether the system is running on AC or battery. To fill this gap, you can create a single udev rule that runs a script when the AC adapter is plugged and unplugged:

Further instructions follow.

My guess is you want to do something like create a fake service called acpluggedin.service which is started and stopped by the udev rule, and then have the mongo and redis services Requires it. Or something.

Answered By: user3113723

There’s a great article about this here: https://chrisdown.name/2017/10/29/adding-power-related-targets-to-systemd.html

Option 1

Basically you have to do following:

Create systemd target for AC power enabled:

cat > /etc/systemd/system/ac.target << 'EOF'
[Unit]
Description=On AC power
DefaultDependencies=no
StopWhenUnneeded=yes
EOF

Create systemd target for running on battery power:

cat > /etc/systemd/system/battery.target << 'EOF'
[Unit]
Description=On battery power
DefaultDependencies=no
StopWhenUnneeded=yes
EOF

Reboot the system or simply run systemctl daemon-reload to load these new configuration files.

Add udev rules to trigger state changes:

cat > /etc/udev/rules.d/99-powertargets.rules << 'EOF'
SUBSYSTEM=="power_supply", KERNEL=="AC", ATTR{online}=="0", RUN+="/usr/sbin/systemctl start battery.target"
SUBSYSTEM=="power_supply", KERNEL=="AC", ATTR{online}=="1", RUN+="/usr/sbin/systemctl start ac.target"
EOF

Now reboot the system or just run udevadm control --reload-rules to activate the newly added udev configuration file.

After this you can query the current system state with

$ sudo systemctl status battery.target
‚óŹ battery.target - On battery power
   Loaded: loaded (/etc/systemd/system/battery.target; static; vendor preset: disabled)
   Active: inactive (dead)

Oct 29 12:24:33 HOSTNAME systemd[1]: Reached target On battery power.
Oct 29 12:24:33 HOSTNAME systemd[1]: battery.target: Unit not needed anymore. Stopping.
Oct 29 12:24:33 HOSTNAME systemd[1]: Stopped target On battery power.

You can then create new systemd service scripts that use these new states. For example

$ systemctl cat powerdown.service
# /etc/systemd/system/powerdown.service
[Unit]
Description=Laptop battery savings

[Service]
Type=oneshot
ExecStart=/usr/local/bin/powerdown

[Install]
WantedBy=battery.target

Option 2

If you don’t really want to play with all the systemd complexity, you can use just the udev part and directly run scripts on that.

cat > /etc/udev/rules.d/99-my-battery-commands.rules << 'EOF'
SUBSYSTEM=="power_supply", KERNEL=="AC", ATTR{online}=="0", RUN+="/usr/local/bin/script-to-run-when-losing-ac-power"
SUBSYSTEM=="power_supply", KERNEL=="AC", ATTR{online}=="1", RUN+="/usr/local/bin/script-to-run-when-ac-power-restored"
EOF

and use udevadm control --reload-rules (and udevadm trigger seems to work, too) to load the new config.

If you want to know in the middle of some shell script if AC power is currently available, just run on_ac_power. It silently returns true exit code if AC power is connected, or false if running on battery. Be warned though that on_ac_power is too stupid to return true if running in system without a batter (e.g. desktop system).

Answered By: Mikko Rantalainen
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.