Keystone SystemsKS Systems

NixOS Laptop Configuration: Power, WiFi, and Tips

Running NixOS on a laptop requires attention to power management, wireless connectivity, and hardware-specific quirks. This guide covers common configurations and gotchas.

Power Management

TLP (Recommended)

TLP provides automatic power optimization:

{
  services.tlp = {
    enable = true;
    settings = {
      # CPU scaling
      CPU_SCALING_GOVERNOR_ON_AC = "performance";
      CPU_SCALING_GOVERNOR_ON_BAT = "powersave";

      # CPU performance limits
      CPU_MIN_PERF_ON_AC = 0;
      CPU_MAX_PERF_ON_AC = 100;
      CPU_MIN_PERF_ON_BAT = 0;
      CPU_MAX_PERF_ON_BAT = 50;

      # Turbo boost
      CPU_BOOST_ON_AC = 1;
      CPU_BOOST_ON_BAT = 0;

      # WiFi power saving
      WIFI_PWR_ON_AC = "off";
      WIFI_PWR_ON_BAT = "on";
    };
  };

  # Conflicts with power-profiles-daemon
  services.power-profiles-daemon.enable = false;
}

Power Profiles Daemon (Alternative)

GNOME-friendly alternative:

{
  services.power-profiles-daemon.enable = true;
  # Don't use with TLP
  services.tlp.enable = false;
}

Switch profiles via GNOME settings or:

powerprofilesctl set power-saver
powerprofilesctl set balanced
powerprofilesctl set performance

Lid Switch Behavior

{
  services.logind = {
    lidSwitch = "suspend";
    lidSwitchDocked = "ignore";  # When external display connected
    lidSwitchExternalPower = "lock";
  };
}

Screen Brightness

{
  programs.light.enable = true;
  # Add user to video group
  users.users.yourname.extraGroups = [ "video" ];
}

Then use:

light -A 10  # Increase 10%
light -U 10  # Decrease 10%
light -S 50  # Set to 50%

WiFi Configuration

NetworkManager (Recommended)

{
  networking.networkmanager.enable = true;
  users.users.yourname.extraGroups = [ "networkmanager" ];

  # Don't use wireless with NetworkManager
  networking.wireless.enable = false;
}

Manage networks via nmcli or GUI applet:

# List available networks
nmcli device wifi list

# Connect to network
nmcli device wifi connect "SSID" password "password"

# Show saved connections
nmcli connection show

iwd Backend

For better WiFi performance and reliability:

{
  networking.networkmanager.wifi.backend = "iwd";

  # iwd settings
  networking.wireless.iwd = {
    enable = true;
    settings = {
      General = {
        EnableNetworkConfiguration = true;
      };
      Network = {
        EnableIPv6 = true;
      };
    };
  };
}

Manual wpa_supplicant

For minimal setups:

{
  networking.wireless = {
    enable = true;
    networks = {
      "HomeWifi" = {
        psk = "password";  # Or use pskRaw for hashed password
      };
      "WorkWifi" = {
        auth = ''
          key_mgmt=WPA-EAP
          eap=PEAP
          identity="user@domain"
          password="secret"
        '';
      };
    };
  };
}

Guest WiFi and Captive Portals

The neverssl.com Trick

Captive portals (hotel WiFi, coffee shops) work by intercepting HTTP traffic. Modern browsers use HTTPS everywhere, which prevents the interception.

Solution: Visit http://neverssl.com

This site intentionally uses plain HTTP, allowing the captive portal to intercept and redirect you to the login page.

Automatic Detection

NetworkManager usually detects captive portals automatically and opens a browser. If not:

{
  # Ensure connectivity checking is enabled
  networking.networkmanager = {
    enable = true;
    # Uses NetworkManager's built-in captive portal detection
  };

  # Or install a dedicated captive portal helper
  programs.captive-browser = {
    enable = true;
    interface = "wlan0";
  };
}

Manual Portal Detection

# Check if behind captive portal
curl -I http://neverssl.com

# If redirected (301/302), you're behind a portal
# Open the redirect URL in a browser

ZFS and Hibernation

The Problem

ZFS does not safely support hibernation. When the system hibernates, pool state is written to RAM image. On resume, if the pool was modified (from another boot), data corruption can occur.

ZFS explicitly refuses to import pools that may be in an inconsistent state.

Workarounds

Option 1: Disable Hibernation

{
  # Only allow suspend, not hibernate
  services.logind.lidSwitch = "suspend";
  services.logind.extraConfig = ''
    HandleHibernateKey=ignore
    HandleSuspendKey=suspend
  '';

  # Disable hibernate entirely
  systemd.targets.hibernate.enable = false;
  systemd.targets.hybrid-sleep.enable = false;
}

Option 2: Swap on Non-ZFS Partition

If you must hibernate, use a separate ext4 or swap partition:

{
  # Swap on dedicated partition, not ZVOL
  swapDevices = [{ device = "/dev/nvme0n1p3"; }];

  boot.resumeDevice = "/dev/nvme0n1p3";
}

This allows hibernation but requires careful pool management.

Option 3: Export Pool Before Hibernate (Manual)

# Before hibernate
zpool export tank
systemctl hibernate

# On resume
zpool import tank

Not practical for daily use.

Recommendation

Use suspend instead of hibernate. With modern SSDs and fast boot, the time difference is minimal, and you avoid corruption risk.

Additional Laptop Tips

Battery Status

{
  # Include power info in status
  services.upower.enable = true;
}

Check via:

upower -i /org/freedesktop/UPower/devices/battery_BAT0

Touchpad Configuration

{
  services.libinput = {
    enable = true;
    touchpad = {
      naturalScrolling = true;
      tapping = true;
      clickMethod = "clickfinger";
      disableWhileTyping = true;
    };
  };
}

External Monitor Handling

{
  # Auto-detect and configure displays
  services.autorandr.enable = true;
}

Save profiles:

autorandr --save docked
autorandr --save mobile

Thermald (Intel)

{
  services.thermald.enable = true;  # Intel thermal management
}

Firmware Updates

{
  services.fwupd.enable = true;
}

Check for updates:

fwupdmgr get-updates
fwupdmgr update

Common Issues

WiFi Drops After Suspend

{
  # Reload WiFi module on resume
  powerManagement.resumeCommands = ''
    modprobe -r iwlwifi
    modprobe iwlwifi
  '';
}

Slow Wake from Suspend

{
  boot.kernelParams = [ "mem_sleep_default=deep" ];
}

Function Keys Not Working

{
  # For ThinkPad
  services.throttled.enable = true;

  # For many laptops
  boot.kernelModules = [ "thinkpad_acpi" ];  # Or appropriate module
}