Wayland compositors and systemd just do not seem to mix well (I have similar issues with Hyprland). Even with the systemd integration stuff in Home Manager modules, I still get start-up-order problems with swayidle, swaylock, etc, even if the dependent services have things like After=graphical-session.target
in their systemd service files.
I have 2 ways that somewhat work for my machines. I have not actually tested any of the below code with your NixOS config, but hopefully they can point you in the right directions.
1. Systemd activation all the way down
(The harder, more complex route, but perhaps neater because systemd handles all the process launching)
From what I can see, systemd activation of services that depend on long-running processes (such as Wayland compositors) requires some form of inter-process communication originating from the long-running process that signals when the process is ready, at which point dependent processes (such as Network Manager applets) can then launch. Otherwise, systemd has no way of knowing the correct time to launch the dependent processes – The list of systemd’s available activation Types summarizes how systemd determines when a service is considered “finished”. For Wayland compositors that must launch, be ready before other processes begin, and then stay running, the only appropriate types are notify
and notify-reload
, but these types require internal support from the compositor.
The best way for compositors to fully support systemd activation is by implementing systemd’s sd_notify function and sending a ready signal. This mean the compositor should be started with its own systemd service file, with Type=notify
. However, Sway’s developer(s) appears to explicitly avoid implementing sd_notify
or some equivalent.
(On a different but related track: having ConditionEnvironment=WAYLAND_DISPLAY
in the systemd service file does nothing for resolving start-up-ordering issues. If the environment variable is not set when systemd decides to launch the service, the service simply fails.)
But, at least for the start-up-ordering problem, you can try sticking in sd_notify
functionality yourself, using the shell command systemd-notify
in the Sway config file.
Unfortunately, sd_notify
does not solve the other important issue about propagating environment variables. You may need to insert systemctl --user import-environment <various env vars>
(possibly among other annoying rituals) in the right places for this method to work fully.
(Untested) Nix code for all the above:
# https://github.com/swaywm/sway/pull/3486#issuecomment-456292899
wayland.windowManager.sway.extraconfig = ''
exec systemd-notify --ready || true
''
Add your own systemd service file for Sway in your Home Manager module:
systemd.user.services.sway = {
Unit = {
Description = "Sway Wayland Compositor";
BindsTo = ["sway-session.target"];
};
Service = {
# You may need an `Environment=` line to propagate environment variables to child processes
Type = "notify";
NotifyAccess = "all"; # Probably need this because a child process actually sends the notify signal
ExecStart="${pkgs.sway}/bin/sway"; # Unforunately, this is IFD (import from derivation)
};
Install = {
# This line causes systemd, by itself, to launch Sway at machine boot. Ignore this line if Sway should be started by a login manager or some other way.
WantedBy = [ "multi-user.target" ];
};
}
Finally, get systemd to launch Network Manager applet strictly after Sway has sent the notification (Note: this does not define the complete network-manager-applet.service
but modifies the existing service file defined in Home Manager’s module):
systemd.user.services.network-manager-applet = {
After = [ "sway.service" ];
};
2. Launch dependent processes through Sway’s config file and not systemd
(The easier route, and probably the one you want if you like to stay sane)
Using Network Manager applet as an example:
Do not start up Network Manager applet directly through systemd (the Home Manager module does this) and instead launch it through Sway.
In the Home Manager part of your NixOS configuration:
systemd.user.services.network-manager-applet.Install = {};
And in the Sway config file:
exec systemctl --user network-manager-applet.service
You may also need systemctl --user import-environment
somewhere too, maybe right before the systemctl --user
line in the Sway config, to deal with environment variable issues.
Hopefully this mess of notes on the nightmare that is running Wayland compositors on machines with systemd might be helpful.