Non-obvious Nix Options

You followed the NixOS installation guide, now what? Awash in a sea of nix options what are you missing in your config?
Non-obvious Nix Options
The NixOS logo from fastfetch

NixOS and Home Manager have plenty of options you can set. This is the result of a diligent community accumulating work over many years. You will not need most of it, but when you do want to configure any particular thing, it's nice to have the choice.

I was looking at my config and noticed I have options that are used broadly across my machines. Over the years I've encountered some of these options that I wish I had known about sooner. Essentially, if I followed the installation guide, what else might I want to do?

Also, NixOS has sane defaults for plenty of options and those evolve over time. I used to set boot.initrd.systemd.enable since it was required to use another option, but as of 26.05 it now defaults to true. So my focus here is on the options I didn't realize I needed to change.


Including some batteries

I often look at Fedora's release notes and the changes page on their Wiki to check the discussion around what they decide to accept. I find Fedora strikes a good balance of not waiting too long to adopt something, and not jumping on something too early. For example, Fedora switched to PipeWire in release 34 and not long after I made the jump.

Some options to check:

services.automatic-timezoned.enable - I went to NeurIPS in California last year and realized the timezone on my laptop didn't automatically adjust. You can alternatively force time.timeZone to false and then manually set it through GNOME or KDE. If you have a desktop you can just set time.timeZone.

services.fstrim.enable - If you have an SSD you probably want to periodically trim the FS and not use discard. Fedora enabled this in 2020. The exception is if you are running BTRFS/ZFS or have LUKS. The Arch Wiki explanation goes over the nuance. On my desktop I am boring and run EXT4.

services.fwupd.enable - Enables the firmware update service for flashing firmware updates from the lovely LVFS to devices. This is especially useful for my Framework laptop since they push firmware updates regularly. See my Framework Laptop 13 DIY Edition setup.

hardware.enableAllFirmware or hardware.enableRedistributableFirmware - both enable binary blob firmware for devices. enableRedistributableFirmware covers unfreeRedistributableFirmware licensed firmware like linux-firmware, and enableAllFirmware is a superset that also includes firmware with license unfree. For example, my desktop only loads firmware for my Intel Wifi, Intel Bluetooth and AMD graphics. All of which could be pulled in with just enableRedistributableFirmware.

The following enables mDNS for .local domains to work:

  services.avahi = {
    enable = true;
    nssmdns4 = true;
    openFirewall = true;
  };

services.printing.enable - See Printing on the Wiki for more options. In combination with the avahi settings you can have network auto discovery which is convenient on a laptop.

hardware.bluetooth.powerOnBoot - Automatically turns Bluetooth on at boot.


Oops I ran out of disk space

Every time you rebuild you add another generation to the system. This is fantastic for rollback, but I didn't realize how much space the Nix store was taking up until one day I was completely full. This is not an uncommon occurrence when people first try Nix.

I could run garbage collection on my own, but I never remember to. Alternatively, I can automate it through nix.gc, but I like to use nh, the Nix helper, which has nh clean. That reimplements nix-collect-garbage while also cleaning gcroots from things like devenv:

  programs.nh = {
    enable = true;
    clean = {
      enable = true;
      dates = "weekly";
      extraArgs = "--keep-since 14d --keep 3 --keep-one";
    };
  };

This will:

  • Run the clean weekly.
  • Remove anything older than two weeks.
  • Keep at least the three most recent generations, regardless of age.
  • Keep any active direnv gcroots.

Note that if you add this in your Home Manager config rather than your system config it will not be able to find all GC roots on the system, which bit me in the past.

nix.settings.auto-optimise-store - Similar to, but different from GC. This will find identical files and replace them with hard-links so there is only one copy.

boot.loader.systemd-boot.configurationLimit - I didn't realize I needed this until I filled up my EFI partition once. I cap mine at 8. This is less important now that I have GC scheduled, but I keep it for good measure.


A couple of Nix gotchas

Nix is its own special beast, with its own set of suprising defaults.

nix.settings.max-jobs and nix.settings.cores - On a desktop or laptop a big rebuild can potentially slow down your interactive applications while it runs. The Nix reference manual has a page on Tuning Cores and Jobs. I use 4 and 4 on my machine. However, as I'm writing this I found out I could set nix.daemonCPUSchedPolicy and nix.daemonIOSchedClass to idle. I haven't tested this, but the config notes it's sensible for interactive systems like desktops and laptops.

boot.kernelPackages = pkgs.linuxPackages_latest; - even if you are on unstable you will not get the latest kernel unless you set this. See the Linux Kernel wiki page.

nix.channel.enable - If you use flakes, you can disable channels. I was confused once when packages were not matching up as I expected and that was because an old channel had been added at one point.

nix.registry.nixpkgs.flake = nixpkgs; - This will ensure that commands like nix shell use the version of nixpkgs you built the system with. Thank you Ian for the recommendation. Just make sure to also set nix.nixPath = ["nixpkgs=flake:nixpkgs"] as per this thread.

programs.nix-index.enable - If you run a program that is not installed you will get a list of the packages that contain the program.


An option for everything

There is an overabundance of options with Nix and I love that. Here is a cursed configuration option that is highly specific to me:

  hardware.display.edid.linuxhw."XL2420G_2014" = ["BNQ7F39" "XL2420G" "2014" "2E7C0DC569A3"];
  hardware.display.outputs."DP-2".edid = "XL2420G_2014.bin";
  hardware.display.outputs."DP-2".mode = "e";

I cannot for the life of me get Linux to properly detect one of my monitors over DisplayPort unless I use this incantation that I figured out thanks to the wonderful EDID repository. You know what though? I love that I can declare this in my config and remember this is how I solved the problem.

Oh and Home Manager has its own set of options! So I'll do a follow up on those in the future.

I write about a variety of projects, events and thoughts, but they all share one aspect: I learned something and want to share my experience with others.

No spam, no sharing to third party. Only you and me.

Member discussion