Yurii M.

NixOS beginner tutorial: zero to flakes

Tuesday, Mar 21, 2023

Are you new to NixOS and want to learn how to manage your desktop installation declaratively? If so, this beginner-friendly tutorial is perfect for you! NixOS is a unique Linux distribution that centers around the Nix package manager. With NixOS, you can create a configuration file just once, and then easily apply it to multiple different systems. It also comes with incredible amount of customizability, 80 000+ packages, and is nearly impossible to break. In this tutorial, you will get started with declarative system management in NixOS.

Note that you should already have some basic understanding of linux to use this distro, and it probably should not be your first one.

OS Installation

NixOS used to have a tedious manual installation, but thanks to the adoption of the popular calamares installer, installing the distro has become straight forward.

The hard part begins after booting up your system for the first time. NixOS has a package management system that is very different from other distros.

While it is technically possible to install packages using the command nix-env -iA packageName, I highly advise against doing so, because you are going to miss out on many advantages of NixOS.

Right after first installation of NixOS, you are going to get a default configuration file at /etc/nixos/configuration.nix. Open it nano or other provided code editor.

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{ config, pkgs, ... }:

{
  imports =
    [
      ./hardware-configuration.nix # importing hardware file from same directory
    ];

  boot.loader.systemd-boot.enable = true; # boot loader, you can change it to grub
  boot.loader.efi.canTouchEfiVariables = true;
  
  users.users.yurii = {
    isNormalUser = true;
    extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
    packages = with pkgs; [
      firefox
      nano
    ];
  };

  services.xserver.enable = true; # enable X11
  services.xserver.displayManager.sddm.enable = true; # login manager
  services.xserver.desktopManager.plasma5.enable = true; # choosing you favourite desktop
                                                         # environment is that easy!

  system.stateVersion = "22.11"; # just don't touch this until you read about this
  

  # note: you can find all available options by running `man configuration.nix`
  
  ...
  
}

Of course you are going to have much more options defined there, however the one that we are interested in right now is “users”. NixOS is going to use your username from the installation phase, but you can define more users.

The .nix syntax might seem a little strange at first, but until you are doing anything advanced, you can think of it as json with functions.

Installing new desktop environments and other software

Now let’s say you don’t like KDE, how would you install GNOME? It’s only the matter of changing one line:

1
2
3
4
5
services.xserver.desktopManager.plasma5.enable = true;

# change to 

services.xserver.desktopManager.gnome.enable = true;

Or what if you want to install some package like neovim? Just add it to your user’s array of packages like follows:

1
2
3
4
5
6
7
8
9
  users.users.yurii = {
    isNormalUser = true;
    extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
    packages = with pkgs; [
      firefox
      nano
      neovim # add here
    ];
  };

To apply these changes you are going to need to rebuild the system. To do it use this command in your terminal:

1
 $ sudo nixos-rebuild switch

What it will do is create a new NixOS generation and switch to it. Reboot, and while your system is loading, you will be able to select previous or current generation. This is possible because NixOS does not actually remove packages unless you tell it to, so all your NixOS generations will add up until you run nix-collect-garbage --delete-older-than 10d

The new generation is clean of all KDE packages, and will function as if you had just installed GNOME.

Temporarily installing software

To try out software on any other distro, you’d have to pollute your environment with packages and dependencies or spinning up containers. Nix simplifies this task a lot, because you can just declare environments that contain variables and packages you need at the moment.

To get a shell with python and nodejs installed run this command:

1
 $ nix-shell -p python nodejs

You will be dropped into a shell that contains those packages, and when you exit it, they will no longer pollute your system. Later they will get garbage collected just like generations.

You can also declare those environments in a .nix file as shown on the NixOS wiki.

Nixpkgs and Nixpkgs-unstable

NixOS has two main channels that you can install software from: nixpkgs and nixpkgs-unstable. First one has stable packages that are updated every six months, while second is a rolling release.

You can search for new packages here.

Manipulating your channels is done through these commands:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
list added channels:

 $ nix-channel --list
 
add unstable channel:

 $ sudo nix-channel --add https://nixos.org/channels/nixpkgs-unstable unstable
 
update all channels:

 $ sudo nix-channel --update
 

BUT WAIT! You might ask:

  • Why would I manage my channels manually if I’m using nix?
  • What if one of my systems has a newer channel version and breaks another one?
  • Can’t I just declare it in a file so it does it automatically?

The answer is of course you can! That’s where the great experimental feature comes into play, and it is called…

NIX FLAKES

Nix flakes is a concept that every newcomer eventually stumbles upon. It might be a hard concept to grasp in the beginning, but it fixes all the problems mentioned above.

They allow you to specify your code’s dependencies, and provide you with lock file just like any other modern package manager would.

Being an experimental feature, flakes first need to be included in your configuration.nix as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{ config, pkgs, ... }:

{
  ...
  
  nix.settings.experimental-features = [ "nix-command" "flakes" ];
  
  ...
  
}

After rebuilding your system, you should have access to nix flakes. Now let’s change configuration.nix file to flake.nix

Doing it is only the matter of adding flake.nix to our /etc/nixos/ directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
  description = "My nixos configuration flake";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-22.11";
    # or this
    # nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
  };

  outputs = { nixpkgs, home-manager, ... }@inputs: {
  
    nixosConfigurations = {
    
      # replace "hostname" with you hostname e.g. nixos or my-system
      hostname = nixpkgs.lib.nixosSystem {
        specialArgs = { inherit inputs; };
        modules = [ ./nixos/configuration.nix ];
      };
      
    };
    
  };
  
}

As you can see, we specify dependencies in “inputs”, and then outputs contains “nixosConfigurations”. We define the channel we want to use in inputs. “nixosConfigurations” is the first place where NixOS is going to look for your configuration, so that’s where we put out configuration.nix module.

To rebuild your system you should now use:

1
2
3
 $ sudo nixos-rebuild --flake /etc/nixos/#hostname switch
 
# or use the old command because nix will prioritize flakes anyway

You can now also temporarily install software from different channels:

1
2
3
4
5
 $ nix shell nixpkgs/nixos-22.11#neovim
 
# and
 
 $ nix shell nixpkgs/nixos-unstable#neovim

Next steps

You should now have some basic understanding of how to work with NixOS, but the journey is far from over. NixOS gives incredible power to those who are not afraid to put in the hours.

Nix flakes are very useful for defining shell environments, derivations and home-configurations.

I highly recommend to look into those, and also reading through nix pills to better understand the nix language.

Nix and NixOS have so much more to them, and it is impossible to tell everything in just one blog post. It really is a fascinating ecosystem whose ideas may be too good to be denied.