Tips&Tricks for NixOS Desktop

It’s often said that git is a poor VCS, but a good VCS Construction Kit. NixOS can feel similar at
times — nix is very powerful, and there are amazing things you can do if you know nuts&bolts of
it. However, for a casual user, the sheer amount of possibilities can feel overwhelming, and it
might be hard to see what’s most useful in the space of possible.

Over the years, I’ve accumulated a bunch of tricks I wish I knew earlier. I want to share them here,
and learn some knew ones, please feel free to add yours!


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 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

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 '' -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).

How do you manage your dotfiles instead?

Using git as an example:

Are you sure it works? Because this issue is still open: NixOS: switch-to-configuration script does not correctly add a boot entry when executed standalone · Issue #82851 · NixOS/nixpkgs · GitHub

Just checked, and, yes, it seems to work for me. That is

  • I rebooted into one of the older configurations by selecting it in the boot manager (systemd-boot)
  • After boot, I run doas /run/current-system/bin/switch-to-configuration boot in the terminal
  • After reboot, that configuration was selected as the default one by the boot loader (I get the same list of entries, but now not the topmost one is focused).

hi all. newbie here. have an unusual issue with generations not visible in the Grub menu? I posted the full issue here: NO generations in grub?
I’m at a loss and perplexed by the issue.
I’ve installed afew flatpaks, printer, scanner and nvidia services in nixos 23.05. Im yet to install bluetooth, mount points and samba.
so far, i like the feel of the system. Was a die hard fedora kde (37) fanboy but their quality control by late has disappointed me, especially f38.

Require help and I guess ive posted into the wrong topic ? but any help be appreciated :flushed:


1 Like

Seriously these advice should be documented somewhere for newcomers :smiley:


If it’s a single user system you don’t really care either way, the packages just need to be present for your user in some way.
On multi-user system this would make more sense because with home-manager you would then have to apply the config to multiple users on the system.
However an additional bonus to home-manager is a lot of options which aren’t part of nixpgks.
In addition if you use home-manager it’s easier to share your configuration with a non-NixOS system.
E.g. if your company uses another distro, etc.

With this I don’t agree at all, especially for new users. If someone is comfortable with NixOS sure then go ahead but at that point they should know how to fix problems.
The nice thing about NixOS is that you easily can install packages from unstable if you need them.
Once you setup the unstable-overlay it’s not really that complicated to install a package with unstable.something.

An important thing to keep in mind here is that switching back to an earlier generation doesn’t rollback the state. So if you have an application that uses a database to store its data and the schema changes between updates your data might be unusable in the previous generation.
Happened to me with Telegram and Signal for example.
Therefore it’s still very important to have backups of your data matching to your generations.

Good advice, as a quick and dirty solution for people with plenty of disk space stream-run might still be the easier option.


As for searching, I also find nix search nixpkgs very usefull. especially for other flakes than nixpkgs.
Another good way is to nix repl and :load-flake

For this I use mainly 2 options: nix-ld and similar tools like nix-alien and steam-run for quick and easy.
As a reminder, nixos recently switched for default chroot method to bwrap, which is almost better in every way except it changes some paths and all other “non user files” get nobody permissions.

Also for an awesome and full info about this, this is a great resourse:

Now for my tips: