From 5513c368ad2d1420cbb65e8a36c77bb172569ec2 Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Sun, 5 Apr 2026 17:22:48 -0400 Subject: [PATCH] nixos/power-management: Handle resume correctly with `sleep.target` Fixes `nixos.tests.hibernate` nondeterministic failure. The documentation in `systemd.special(7)` recommends using this `StopWhenUnneeded` method, rather than using custom unit logic post-resume. This method is sufficiently general, and matches systemd's typical model of using `ExecStart` and `ExecStop` as duals that mirror each others processes across symmetric events in the system's life cycle, e.g. bootup / shutdown. This involved removing `post-resume.target`. This was introduced in d5604f0b22daf8a0ec341c70607f014dfc9bf207 as a way to notify services of resume events, but as discussed, this is not the typical model. See: https://github.com/NixOS/nixpkgs/pull/488429/changes#r3038005884 --- .../manual/release-notes/rl-2605.section.md | 2 + nixos/modules/config/power-management.nix | 57 +++++-------------- nixos/modules/services/hardware/undervolt.nix | 22 +++++-- 3 files changed, 33 insertions(+), 48 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index 5220d49e71e2..1d50b7b8d257 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -137,6 +137,8 @@ - `services.uptime` has been removed because the package it relies on does not exist anymore in nixpkgs. +- `post-resume.target` has been removed. See {manpage}`systemd.special(7)` about `sleep.target` for instructions on ordering a process after resume with `ExecStop=`. + - `services.kubernetes.addons.dns.coredns` has been renamed to `services.kubernetes.addons.dns.corednsImage` and now expects a package instead of attrs. Now, by default, nixpkgs.coredns in conjunction with dockerTools.buildImage is used, instead of pulling the upstream container image from Docker Hub. If you want the old behavior, you can set: diff --git a/nixos/modules/config/power-management.nix b/nixos/modules/config/power-management.nix index 202ecdb7cffb..9d015c021794 100644 --- a/nixos/modules/config/power-management.nix +++ b/nixos/modules/config/power-management.nix @@ -90,68 +90,35 @@ in https://www.freedesktop.org/software/systemd/man/latest/systemd.special.html#sleep.target ''; - systemd.targets.post-resume = { - description = "Post-Resume Actions"; - requires = [ "post-resume.service" ]; - after = [ "post-resume.service" ]; - wantedBy = [ "sleep.target" ]; - unitConfig.StopWhenUnneeded = true; - }; - systemd.services = { # Service executed before suspending/hibernating. - pre-sleep = { - description = "Pre-Sleep Actions"; + sleep-actions = { + description = "Sleep Actions"; wantedBy = [ "sleep.target" ]; before = [ "sleep.target" ]; + unitConfig.StopWhenUnneeded = true; script = '' # NixOS pre-sleep script # config.powerManagement.powerDownCommands ${cfg.powerDownCommands} ''; - serviceConfig.Type = "oneshot"; - }; - - # Service executed after resuming from suspend/hibernate - post-resume = { - description = "Post-Resume Actions"; - # Pulled in by post-resume.service above - after = [ "sleep.target" ]; - script = '' + preStop = '' # NixOS pre-resume script - /run/current-system/systemd/bin/systemctl try-restart --no-block post-resume.target - # config.powerManagement.resumeCommands ${cfg.resumeCommands} # config.powerManagement.powerUpCommands ${cfg.powerUpCommands} ''; - serviceConfig.Type = "oneshot"; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; }; - # Service executed before shutdown - pre-shutdown = { - description = "Pre-Shutdown Actions"; - wantedBy = [ - "shutdown.target" - ]; - before = [ - "shutdown.target" - ]; - script = '' - # NixOS pre-shutdown script - - # config.powerManagement.powerDownCommands - ${cfg.powerDownCommands} - ''; - serviceConfig.Type = "oneshot"; - unitConfig.DefaultDependencies = false; - }; - - # Service executed after boot + # Service executed after boot, and stopped during shutdown post-boot = { description = "Post-Boot Actions"; # It's not well defined at what point in the bootup sequence this should run @@ -167,6 +134,12 @@ in # config.powerManagement.powerUpCommands ${cfg.powerUpCommands} ''; + preStop = '' + # NixOS pre-shutdown script + + # config.powerManagement.powerDownCommands + ${cfg.powerDownCommands} + ''; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; diff --git a/nixos/modules/services/hardware/undervolt.nix b/nixos/modules/services/hardware/undervolt.nix index a2c16f9bd8de..585364e24e34 100644 --- a/nixos/modules/services/hardware/undervolt.nix +++ b/nixos/modules/services/hardware/undervolt.nix @@ -194,12 +194,7 @@ in systemd.services.undervolt = { description = "Intel Undervolting Service"; - # Apply undervolt on boot, nixos generation switch and resume - wantedBy = [ - "multi-user.target" - "post-resume.target" - ]; - after = [ "post-resume.target" ]; # Not sure why but it won't work without this + wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; @@ -208,6 +203,21 @@ in }; }; + systemd.services.undervolt-sleep = { + description = "Preserve Intel Undervolting After Sleep"; + + wantedBy = [ "sleep.target" ]; + before = [ "sleep.target" ]; + + unitConfig.StopWhenUnneeded = true; + serviceConfig = { + Type = "oneshot"; + Restart = "no"; + RemainAfterExit = true; + ExecStop = "${cfg.package}/bin/undervolt ${toString cliArgs}"; + }; + }; + systemd.timers.undervolt = lib.mkIf cfg.useTimer { description = "Undervolt timer to ensure voltage settings are always applied"; partOf = [ "undervolt.service" ];