Systemd – How to Run Systemd Service After Display Manager

gnomesystemdsystemd-service

I have a script that changes some Gnome settings which I want to run on startup. The script itself works fine when I run it manually, but I get the following error when I run it as a systemd service:

● startup-user.service - Startup Service
     Loaded: loaded (/etc/systemd/system/startup-user.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Sat 2022-02-05 13:03:48 UTC; 21s ago
    Process: 1948 ExecStart=/data/startup-user.sh (code=exited, status=1/FAILURE)
   Main PID: 1948 (code=exited, status=1/FAILURE)
        CPU: 22ms

Feb 05 13:03:48 debian systemd[1]: Started Startup Service.
Feb 05 13:03:48 debian gsettings[1950]: failed to commit changes to dconf: Cannot autolaunch D-Bus without X11 $DISPLAY
Feb 05 13:03:48 debian gsettings[1953]: failed to commit changes to dconf: Cannot autolaunch D-Bus without X11 $DISPLAY
Feb 05 13:03:48 debian gsettings[1956]: failed to commit changes to dconf: Cannot autolaunch D-Bus without X11 $DISPLAY
Feb 05 13:03:48 debian startup-user.sh[1959]: Error connecting: Cannot autolaunch D-Bus without X11 $DISPLAY
Feb 05 13:03:48 debian systemd[1]: startup-user.service: Main process exited, code=exited, status=1/FAILURE
Feb 05 13:03:48 debian systemd[1]: startup-user.service: Failed with result 'exit-code'.

Here is the script file:

#!/bin/bash

gsettings set org.gnome.desktop.peripherals.touchpad natural-scroll false
gsettings set org.gnome.desktop.peripherals.touchpad tap-to-click true
gsettings set org.gnome.settings-daemon.plugins.power ambient-enabled false
gdbus call --session --dest org.gnome.SettingsDaemon.Power --object-path /org/gnome/SettingsDaemon/Power --method org.freedesktop.DBus.Properties.Set org.gnome.SettingsDaemon.Power.Screen Brightness '<int32 100>'

And here's the service unit file:

[Unit]
Description=Startup Service
After=graphical.target

[Service]
Type=simple
ExecStart=/data/startup-user.sh
User=user

[Install]
WantedBy=graphical.target

This is the first time I've worked with systemd services, and I don't know much about display managers or whatever else is involved here, but I assume that if my service is starting after graphical.target, then the display manager should be started and all necessary variables should be set. I've also tried After=gdm.service, and I'm not sure whether I'm supposed to have graphical.target for both After= and WantedBy=, but if I remove it from WantedBy= then the service doesn't run at all.

In case you're wondering and/or it makes a difference, this is for a live system I'm building with Debian Live. Instead of using a persistence partition, I want to run this script every time I boot up so it can do some basic configuration. The script file resides on a separate ext4 partition mounted at /data, so I can modify the script file whenever I want to change the settings.

What am I doing wrong? Thanks!

Best Answer

The problem here isn't just "before/after", it's also that not all state is global. Just because X11 has already been started doesn't mean all processes automatically gain knowledge of what $DISPLAY to use, and the same applies to the session D-Bus socket address. (Especially not when you remember that there could be multiple sessions, each having a different $DISPLAY value.) Instead, this is limited to the process tree starting with the display manager itself and its children.

(System services in general are not meant to poke around in user sessions.)

Session components should be run by the session itself – either through XDG Autostart (using .desktop files in ~/.config/autostart/ and the corresponding /etc/xdg location) or through systemd user services (~/.config/systemd/user) or through the Xsession script if X11 is used (~/.xprofile, ~/.xsession). That way they always run at the correct time and always inherit the correct environment.