diff --git a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml index cd2ad54db20..2d0129d3601 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml @@ -539,6 +539,21 @@ Add udev rules for the Teensy family of microcontrollers. + + + systemd-oomd is enabled by default. Depending on which systemd + units have ManagedOOMSwap=kill or + ManagedOOMMemoryPressure=kill, systemd-oomd + will SIGKILL all the processes under the appropriate + descendant cgroups when the configured limits are exceeded. + NixOS does currently not configure cgroups with oomd by + default, this can be enabled using + systemd.oomd.enableRootSlice, + systemd.oomd.enableSystemSlice, + and + systemd.oomd.enableUserServices. + + The pass-secret-service package now diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md index 119cd12492a..640a15cb242 100644 --- a/nixos/doc/manual/release-notes/rl-2211.section.md +++ b/nixos/doc/manual/release-notes/rl-2211.section.md @@ -182,6 +182,15 @@ Use `configure.packages` instead. - Add udev rules for the Teensy family of microcontrollers. +- systemd-oomd is enabled by default. Depending on which systemd units have + `ManagedOOMSwap=kill` or `ManagedOOMMemoryPressure=kill`, systemd-oomd will + SIGKILL all the processes under the appropriate descendant cgroups when the + configured limits are exceeded. NixOS does currently not configure cgroups + with oomd by default, this can be enabled using + [systemd.oomd.enableRootSlice](options.html#opt-systemd.oomd.enableRootSlice), + [systemd.oomd.enableSystemSlice](options.html#opt-systemd.oomd.enableSystemSlice), + and [systemd.oomd.enableUserServices](options.html#opt-systemd.oomd.enableUserServices). + - The `pass-secret-service` package now includes systemd units from upstream, so adding it to the NixOS `services.dbus.packages` option will make it start automatically as a systemd user service when an application tries to talk to the libsecret D-Bus API. - There is a new module for AMD SEV CPU functionality, which grants access to the hardware. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index cb3599589cf..f7ceddeb903 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1229,6 +1229,7 @@ ./system/boot/systemd/journald.nix ./system/boot/systemd/logind.nix ./system/boot/systemd/nspawn.nix + ./system/boot/systemd/oomd.nix ./system/boot/systemd/shutdown.nix ./system/boot/systemd/tmpfiles.nix ./system/boot/systemd/user.nix diff --git a/nixos/modules/system/boot/systemd/oomd.nix b/nixos/modules/system/boot/systemd/oomd.nix new file mode 100644 index 00000000000..a2afc4d7ce2 --- /dev/null +++ b/nixos/modules/system/boot/systemd/oomd.nix @@ -0,0 +1,57 @@ +{ config, lib, ... }: let + + cfg = config.systemd.oomd; + +in { + options.systemd.oomd = { + enable = lib.mkEnableOption "the systemd-oomd OOM killer" // { default = true; }; + + # Fedora enables the first and third option by default. See the 10-oomd-* files here: + # https://src.fedoraproject.org/rpms/systemd/tree/acb90c49c42276b06375a66c73673ac351025597 + enableRootSlice = lib.mkEnableOption "oomd on the root slice (-.slice)"; + enableSystemSlice = lib.mkEnableOption "oomd on the system slice (system.slice)"; + enableUserServices = lib.mkEnableOption "oomd on all user services (user@.service)"; + + extraConfig = lib.mkOption { + type = with lib.types; attrsOf (oneOf [ str int bool ]); + default = {}; + example = lib.literalExpression ''{ DefaultMemoryPressureDurationSec = "20s"; }''; + description = '' + Extra config options for systemd-oomd. See man oomd.conf + for available options. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.additionalUpstreamSystemUnits = [ + "systemd-oomd.service" + "systemd-oomd.socket" + ]; + systemd.services.systemd-oomd.wantedBy = [ "multi-user.target" ]; + + environment.etc."systemd/oomd.conf".text = lib.generators.toINI {} { + OOM = cfg.extraConfig; + }; + + systemd.oomd.extraConfig.DefaultMemoryPressureDurationSec = lib.mkDefault "20s"; # Fedora default + + users.users.systemd-oom = { + description = "systemd-oomd service user"; + group = "systemd-oom"; + isSystemUser = true; + }; + users.groups.systemd-oom = { }; + + systemd.slices."-".sliceConfig = lib.mkIf cfg.enableRootSlice { + ManagedOOMSwap = "kill"; + }; + systemd.slices."system".sliceConfig = lib.mkIf cfg.enableSystemSlice { + ManagedOOMSwap = "kill"; + }; + systemd.services."user@".serviceConfig = lib.mkIf cfg.enableUserServices { + ManagedOOMMemoryPressure = "kill"; + ManagedOOMMemoryPressureLimit = "50%"; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 1cf310cb332..0acce5ef799 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -561,6 +561,7 @@ in { systemd-networkd-ipv6-prefix-delegation = handleTest ./systemd-networkd-ipv6-prefix-delegation.nix {}; systemd-networkd-vrf = handleTest ./systemd-networkd-vrf.nix {}; systemd-nspawn = handleTest ./systemd-nspawn.nix {}; + systemd-oomd = handleTest ./systemd-oomd.nix {}; systemd-shutdown = handleTest ./systemd-shutdown.nix {}; systemd-timesyncd = handleTest ./systemd-timesyncd.nix {}; systemd-misc = handleTest ./systemd-misc.nix {}; diff --git a/nixos/tests/systemd-oomd.nix b/nixos/tests/systemd-oomd.nix new file mode 100644 index 00000000000..f0b5a5f8e01 --- /dev/null +++ b/nixos/tests/systemd-oomd.nix @@ -0,0 +1,37 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +{ + name = "systemd-oomd"; + + nodes.machine = { pkgs, ... }: { + systemd.oomd.extraConfig.DefaultMemoryPressureDurationSec = "1s"; # makes the test faster + # Kill cgroups when more than 1% pressure is encountered + systemd.slices."-".sliceConfig = { + ManagedOOMMemoryPressure = "kill"; + ManagedOOMMemoryPressureLimit = "1%"; + }; + # A service to bring the system under memory pressure + systemd.services.testservice = { + serviceConfig.ExecStart = "${pkgs.coreutils}/bin/tail /dev/zero"; + }; + # Do not kill the backdoor + systemd.services.backdoor.serviceConfig.ManagedOOMMemoryPressure = "auto"; + + virtualisation.memorySize = 1024; + }; + + testScript = '' + # Start the system + machine.wait_for_unit("multi-user.target") + machine.succeed("oomctl") + + # Bring the system into memory pressure + machine.succeed("echo 0 > /proc/sys/vm/panic_on_oom") # NixOS tests kill the VM when the OOM killer is invoked - override this + machine.succeed("systemctl start testservice") + + # Wait for oomd to kill something + # Matches these lines: + # systemd-oomd[508]: Killed /system.slice/systemd-udevd.service due to memory pressure for / being 3.26% > 1.00% for > 1s with reclaim activity + machine.wait_until_succeeds("journalctl -b | grep -q 'due to memory pressure for'") + ''; +})