Cannot start systemd services: Failed to execute child process "dbus-launch"

It’s some days that I’m trying to get done this little systemd service, but my brain is melting. Can someone help me pls?

I use flake and home-manager. I searched multiple times online but found not very much.

This is my systemd service that should check the battery status with the timer every 5 min. and check if the battery is under 10%, I edited the if statement to check “less than 80” only for debugging purpose.

{ pkgs, ... }:
{
  systemd.timers."low-battery-notification" = {
    wantedBy = [ "timers.target" ];
    timerConfig = {
      OnBootSec = "5m";
      OnUnitActiveSec = "5m";
      Unit = "low-battery-notification.service";
    };
  };

  systemd.services."low-battery-notification" = {
    description = "Low Battery Notfication: check battery state every 5 min";
    environment = { DISPLAY = ":0"; };
    script = ''
	  #!/usr/bin/env ${pkgs.bash}

	  # Flag file to track if the notification has already been sent
	  NOTIFY_SENT_FLAG="/tmp/battery_low_notify_sent"

	  # Function to check battery status and send notification
	  check_battery() {
	      # Get battery percentage using `acpi` command (you may need to install acpi)
	      BATTERY_PERCENTAGE=$(${pkgs.acpi}/bin/acpi -b | grep -P -o '[0-9]+(?=%)')

	      # If battery percentage is less than 10%
	      if [ "$BATTERY_PERCENTAGE" -lt 80 ]; then
		  # Check if the notification has already been sent
		  if [ ! -f "$NOTIFY_SENT_FLAG" ]; then
		      # Send a notification
		      ${pkgs.libnotify}/bin/notify-send -u critical "Battery Low" "Battery level is under 10%. Plug in your charger!"

		      # Create the flag file to prevent further notifications
		      touch "$NOTIFY_SENT_FLAG"
		  fi
	      else
		  # If battery is above 10%, remove the flag file (if it exists)
		  if [ -f "$NOTIFY_SENT_FLAG" ]; then
		      rm "$NOTIFY_SENT_FLAG"
		  fi
	      fi
	  }

	  # Call the function to check battery
	  check_battery
      '';
    serviceConfig = {
      Type = "oneshot";
      User = "root";
    };
  };
}

When I start this service with systemctl start low-battery-notification.service I get this error in the journal

Oct 02 10:42:37 anon low-battery-notification-start[70339]: Failed to execute child process “dbus-launch” (No such file or directory)
Oct 02 10:42:37 anon systemd[1]: low-battery-notification.service: Main process exited, code=exited, status=1/FAILURE

I’ve searched online and someone suggest to install dbus-x11, but there is no package like this in nixpkgs, the most similar one is dbus but it’s already installed and also dbus-launch works.

You’ll need to add paths = [ pkgs.dbus ]; to the service, and export $(dbus-launch) to the start of your script. Heads up, I still couldn’t get notify-send working with a system service because of the way that dbus is looking for registered notification apps, so I had to run as a user service.

Thank you, in the end I solved this by using services.batsignal

Here’s a working solution:

{ config, pkgs, lib, ... }:

{

  options = {
    services.battery-check.enable = lib.mkEnableOption "battery status daemon";

    services.battery-check.name = lib.mkOption {
      type = lib.types.str;
      description = "Name of the battery";
    };

  };

  config = {
    # command to send system-wide notifications
    nixpkgs.overlays = lib.singleton (self: super:
      { notify = self.writers.writeDashBin "notify" ''
          export PATH=${with pkgs; lib.makeBinPath [ libnotify procps ]}:$PATH

          # get the logged-in X user
          xorg_pid=$(pgrep -x X | head)
          display=$(ps --no-headers -o cmd:1 -p "$xorg_pid" | grep -Po ' \K(:[0-9]) ')
          uid=$(ps --no-headers -o uid:1 -p "$xorg_pid")

          /run/wrappers/bin/sudo -u "#$uid" "DISPLAY=$display" \
            "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus" \
            notify-send "$@" || true
        '';
      });

    systemd.services.battery-check =
    let cfg = config.services.battery-check;
    in lib.mkIf cfg.enable {
      path = with pkgs; [ systemd notify ];
      script = ''
        low=25
        critical=15

        # ignore if charging
        status=$(< /sys/class/power_supply/${cfg.name}/status)
        test "$status" = Charging && exit 0

        level=$(< /sys/class/power_supply/${cfg.name}/capacity)
        if test "$level" -lt $critical; then
          for i in $(seq 2 2 10 | tac); do
            notify -u critical -a system battery "hibernating in $i..." \
              -h string:x-canonical-private-synchronous:x
            sleep 2
          done
          systemctl hibernate
        elif test "$level" -lt $low; then
          notify -u critical -a system battery "$level% battery!"
        fi
      '';
    };

    systemd.timers.battery-check = {
      wantedBy = [ "timers.target" ];
      timerConfig.OnActiveSec = "5m";      # since timer has started
      timerConfig.OnUnitActiveSec = "5m";  # since service last run
    };

  };

}