Tips&Tricks for NixOS Desktop

Use NixOS-unstable

When starting with NixOS, I picked the stable channel, but soon realized that, for the applications I use daily, I’d rather have more up-to-date versions. So I created a complex setup where most things are from stable, and select apps are from unstable.

Some years later, I realized that a better approach for me is to just use NixOS-unstable. Unstable is a misnomer – it’s just a rolling, continuous release, which is gated by a test suite and is pretty stable in practice. What’s more, even if something break’s, its trivial to boot the previous generation. NixOS mitigates major drawbacks of a rolling release model, making it a reasonable
boring default to use on a desktop.

Don’t rollback; switch to a working config instead

Speaking of rollbacks, nixos-rebuild --rollback switch is not the best way to do that. It rolls you back from a currently broken configuration, which requires you to boot the broken config and do some mental gimnastics to calculate how far to rollback.

Instead, boot into the configuration you want to rollback to, and issue

console /run/current-system/bin/switch-to-configuration boot

to make it default.

Use https://search.nixos.org/packages to look for packages

I’ve spent a lot of time looking for a good way to search for nix packages on the command line (eg, using nox utility). But it looks like using NixOS Search is a better default (would love a cli frontend for that though).

Look for options before packages

Many larger things, like docker or emacs, have both a package and an option.

Most of the time, you want the option, as it enables additional integration with the system (eg, starts systemd daemon or what not).

More generally, whatever you want to do with NixOS, start with looking for an option, chances are the thing has first-class support in NixOS.

Don’t use nix-env / nix profile install on NixOS

NixOS has two ways to install the packages. Declaring a package in configuration.nix installs is “globally”, such that it is available to all users. Using nix-env / nix profile install installs the package for the current user.

Originally, I used nix-env to install most packages, and only gradually moved “important” ones to configuration.nix. This seemed “reasonable”, but just made the state of the system less reproducible.

These days, I don’t use nix-env at all. If I need a one-off utility, I use nix shell nixpkgs#ffmpeg to temporary get it. For anything which I need more than a couple of times, I add it
to my system-wide configuration.

As an aside, this reasoning makes me hesitant to try home manager. HM manages user-specific packages, but for my single-user install I don’t want to have user-specific packages in the first
place.

Use buildFHSUserEnv to run unmodified binaries on NixOS

NixOS has a problem with running random binaries from the internet, because it doesn’t have expected things like dynamic linker in a usual place. One way to solve this is to use patchelf to modify the binaries. A different approach is to create a lightweight container around the binary, which sets up a “usual” environment. NixOS has a tool for that, buildFHSUserEnv, but it might get tricky to use. Here’s how I set it up:

environment.systemPackages = [
  (let base = pkgs.appimageTools.defaultFhsEnvArgs; in 
   pkgs.buildFHSUserEnv (base // {
     name = "fhs";
     targetPkgs = pkgs: (base.targetPkgs pkgs) ++ [pkgs.pkg-config]; 
     profile = "export FHS=1"; 
     runScript = "fish"; 
     extraOutputsToInstall = ["dev"];
   }))
   …
]

And here’s how I use it

# Get random binary from the Internet 
$ wget 'https://code.visualstudio.com/sha/download?build=stable&os=linux-x64' -O code.tar.gz 
$ aunpack code.tar.gz && cd VSCode-linux-x64

# Running stuff directly fails 
$ ./bin/code 
./bin/code: line 62: ./bin/../code: cannot execute:
required file not found

# Activating FHS drops me in a shell which looks like a "normal" Linux 
$ fhs 
(fhs) $ ls /usr/bin
(fhs) $ ./bin/code

Some details on the buildFHSUserEnv invocation.

  1. By default, it doesn’t include any packages.
    Building your own env with X and whatnot would be chore. So, I base my FHS on appimage one, using
    pkgs.appimageTools.defaultFhsEnvArgs as a base
  2. name would end up being the name of the binary I use to drop into env
  3. In targetPkgs, I add my own packages.
  4. profile is just for me to be able to add (fhs) to the shell prompt.
  5. runScript to run my shell, rather than bash.
  6. extraOutputsToInstall, like pkg-config, I
    need to be able to compile stuff (dont’ remeber details at this point).
48 Likes