# hdparm - get/set SATA/IDE device parameters # -q Handle the next option quietly, suppressing normal output (but not error messages). # -I Print device information. # -C Check the current IDE power mode status: # unknown (drive does not support this command) # active/idle (normal operation) # standby (low power mode, drive has spun down) # sleeping (lowest power mode, drive is completely shut down) # -S Set the standby (spindown) timeout for the drive. # The timeout specifies how long to wait in idle (with no disk activity) before turning off the motor to save power. # The value of 0 disables spindown, # the values from 1 to 240 specify multiples of 5 seconds # and values from 241 to 251 specify multiples of 30 minutes. # -y Force an IDE drive to immediately enter the low power consumption standby mode, usually causing it to spin down. # Get power status: # sudo hdparm -C /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0CPF0N1 # #=> drive state is: active/idle # # Spin down after 2 minutes (120s/5s = 24). # TODO: Western Digital (WD) Red/Green drives don't work properly. For low `-S` values, they spin down after **10** minutes. And for high values they don't spin down at all. # - https://superuser.com/questions/1746074/spin-down-not-working-on-wd-red # - https://askubuntu.com/questions/196473/setting-sata-hdd-spindown-time-for-western-digital-green-drives # # sudo hdparm -S 24 /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0CPF0N1 # 4tb1 # sudo hdparm -S 24 /dev/disk/by-id/ata-WDC_WD30EFRX-68EUZN0_WD-WCC4N1173157 # 3tb1 # sudo hdparm -S 24 /dev/disk/by-id/ata-WDC_WD30EFRX-68EUZN0_WD-WMC4N0564095 # 3tb2 # # ... after > 10 minutes ... # # Query disk without waking it up: # https://wiki.archlinux.org/title/hdparm#Querying_the_status_of_the_disk_without_waking_it_up # sudo smartctl -i -n standby /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0CPF0N1 # #=> Device is in STANDBY mode, exit(2) # # Get power status: # sudo hdparm -C /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0CPF0N1 # sudo hdparm -C /dev/disk/by-id/ata-WDC_WD30EFRX-68EUZN0_WD-WCC4N1173157 # sudo hdparm -C /dev/disk/by-id/ata-WDC_WD30EFRX-68EUZN0_WD-WMC4N0564095 # #=> drive state is: standby # Power savings. # With 12tb1, 3tb1, 3tb2 spinning and 4tb1 spun-down: 26.1 W # With 12tb1 spinning and 3tb1, 3tb2, 4tb1 spun-down: 19.8 W # -> WD30EFRX measured idle spinning vs standby = +3.15 W # -> WD30EFRX advertised idle spinning vs unplugged = +3.0 W # See also: Systemd service to spin down after boot. https://wiki.archlinux.org/title/hdparm#Putting_a_drive_to_sleep_directly_after_boot # TODO: For external (USB-)disks, use `hd-idle`. # https://www.reddit.com/r/NixOS/comments/751i5t/comment/do3f3l7/ # # Options: # # -a : Select disk for subsequent parameters. # # -i 120: Spin down after 2 minutes (120 seconds) of inactivity. # ExecStart = "${pkgs.hd-idle}/bin/hd-idle -a /dev/disk/by-id/XXX-XXX-XXX -i 120"; # Note about the `script` settings below. # If the disk is spun-down (in standby mode): # - The standby timeout has been set before already, otherwise it would be spinning. # - Don't set the standby timeout again, as this will spin up the disk. { lib, config, options, pkgs, modulesPath, ... }: with lib; let cfg = config.yoda.spin-down-hdd; in { options = { yoda.spin-down-hdd = mkOption { type = types.listOf types.str; default = [ ]; example = ["ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0CPF0N1"]; description = '' List with IDs (/dev/disk/by-id/) of HDDs to spin down after 10 minutes. ''; }; }; config = mkIf (length cfg > 0) { # TODO: Is this required or is it enough to use `${pkgs.hdparm}` in the systemd service script? environment.systemPackages = with pkgs; [ hdparm ]; # For each element (e.g. "ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0CPF0N1") in `cfg` create this config: # systemd.services."hdparm-ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0CPF0N1" = { # description = ...; # wantedBy = ...; # ... # }; systemd.services = builtins.listToAttrs (builtins.map (id: { name = "hdparm-${id}"; value = { description = "Spin down inactive HDD"; wantedBy = [ "multi-user.target" ]; serviceConfig.Type = "oneshot"; script = '' ${pkgs.hdparm}/bin/hdparm -C /dev/disk/by-id/${id} | ${pkgs.busybox}/bin/grep 'standby' || ${pkgs.hdparm}/bin/hdparm -S 24 /dev/disk/by-id/${id} ''; }; }) cfg); }; }