learning nix

Getting started

Using https://zero-to-nix.com/ as the tutorial. It’s using another script to install nix instead of the official one because they claim it provides a better user experience by enabling you to undo, in a single command, all the system changes introduced during the installation process.

# pulled some nix package, built the ponysay package and run the executable
echo 'Hello, world' | nix run 'nixpkgs#ponysay'
 
# define development environment (it will spawn a shell with git and curl)
nix develop 'github:DeterminateSystems/zero-to-nix#example'
 
# you can execute directly the command
nix develop 'github:DeterminateSystems/zero-to-nix#example' --command git help

Recommended to use direnv along with nix.

Build

$ # build a package into the /nix/store/<hash>-bat-<version>, and have a symlink `result` in the current directory
$ nix build 'nixpkgs#bat'
$ ls -alh
drwxr-xr-x l-lin l-lin 4.0 KB Sat Jun  1 18:32:10 2024 .
drwxr-xr-x l-lin l-lin 4.0 KB Sat Jun  1 18:32:00 2024 ..
lrwxrwxrwx l-lin l-lin  54 B  Sat Jun  1 18:32:10 2024 result /nix/store/ghqlmx96vqiywgr0bixsk78z0515drgj-bat-0.24.0

Nix Flakes

A Nix flake is a directory with a flake.nix and flake.lock at the root that outputs Nix expressions that others can use to do things like build packages, run programs, use development environments, or stand up NixOS systems. If necessary, flakes can use the outputs of other flakes as inputs.

  • flake.nix: defines the flake for your project
  • flake.lock: pins all flake inputs, i.e. nix dependencies

When inside a folder containing a flake.nix and flake.lock, I can use nix develop to open a shell using the flakes defined in those files.

Flake references

A flake reference is a string representation of where the flake is located. Flake references are used in two places:

  • In flake input declarations to depend on outputs from the flake.
  • In shell environments when running commands like nix run github:DeterminateSystems/flake-checker (which runs the flake-checker program).

nixpkgs is a flake reference to the NixOS/nixpkgs.

Nix packages

Search for nix packages using:

nix search nixpkgs cargo
# output in json
nix search nixpkgs cargo --json
 
nix flake show github:nix-community/nixpkgs-wayland

Warning

It’s displaying legacyPackage, but it’s not legacy per se. I think it’s a hack by Nix to use something that can be omited by the tool. More information here: https://github.com/NixOS/nixpkgs/blob/fcc8ff7cc271c9652623dae2a9fcd1ba49232b57/flake.nix#L47-L55

When to use nix search vs nix flake show

Should you use nix flake show or nix search? A good rule of thumb is to always use nix search with Nixpkgs and to initially use nix flake show with other flakes. If the package outputs for nix flake show are big enough to be tricky to navigate, use nix search for that flake instead.

FlakeHub

FlakeHub

FlakeHub is a platform for discovering and publishing Nix flakes built by Determinate Systems. It offers a wide variety of features that Nix on its own does not:

  • Semantic versioning for flakes, including version modifiers like ~ (flexible patch) and = (exact match).
  • The ability to explore the Nix flake landscape in a variety of ways:
    • Plaintext search
    • All flakes
    • All organizations
    • Via labels such as nixos or rust
  • Automated flake publishing with GitHub Actions, including a user-friendly wizard to help you construct an Actions configuration for your project.

FlakeHub also offers a CLI tool called fh that you can use to perform a variety of actions against the FlakeHub API.

Documentation - Tutorials

There are lots of Nix documentation, but it’s quite hard to find the “right” one depending on your level of understanding of Nix. The official Nix documentation delves a bit too much on the concepts (for the right reasons), whereas I just want to make something work fast.

I found Evertras introduction to home-manager is the best documentation to start with Nix, along with https://zero-to-nix.com/.

Search packages

nix search nixpkgs your_package

References

Tutorials

Example of nix config

NixOS

installing NixOS on VirtualBox

Issues encountered ✅

Collision when installing Neovim with home-manager ✅

[l-lin@nixos:~/.config/nix-config]$ home-manager switch --flake '.#l-lin'
warning: Git tree '/home/l-lin/.config/nix-config' is dirty
error: builder for '/nix/store/pb4w294qqmd1ymvayz1cvag6v209xggj-home-manager-path.drv' failed with exit code 255;
       last 1 log lines:
       > error: collision between `/nix/store/24vjg0ybgsfppgbs462lzc7m3zhvp0nq-neovim-0.9.5/bin/nvim' and `/nix/store/mfpmj3jis8jp8d05g558fmkznb6yhvxi-neovim-0.9.5/bin/nvim'
       For full logs, run 'nix log /nix/store/pb4w294qqmd1ymvayz1cvag6v209xggj-home-manager-path.drv'.
error: 1 dependencies of derivation '/nix/store/555saq5n44siagb3manqb1szlpim1r35-home-manager-generation.drv' failed to build

Success

https://discourse.nixos.org/t/home-manager-neovim-collision/16963

No need to add to home.packages if I already added to programs.neovim.enable.

Alacritty default shell 🚧

I’m using alacritty as my terminal emulator and I set the default shell to tmux like this:

[shell]
program = "/bin/tmux"
args = ["-2", "-u"]

However, with NixOS, tmux is not available in the path /bin.

Tmux default shell 🤔

I’m setting the default shell in tmux with the following in `~/.tmux.conf:

# Set default shell
set -g default-shell /bin/zsh

However, with NixOS, tmux is not available in the path /bin.

I created a nix module with the following:

{ pkgs, lib, ...}:
{
  programs.tmux = {
    enable = true;
    shell = "${pkgs.zsh}/bin/zsh";
    extraConfig = ''
      ${builtins.readFile ./tmux.conf}
    '';
  };
}

and have the content of tmux.conf without the set -g default shell /bin/zsh.

Not sure if it’s the right way to do it?

Insufficient permission to commit on nix-config folder ✅

When I tried to commit my file in the ~/.config/nix-config folder, I got the following errors for some reason:

error: insufficient permission for adding an object to repository database .git/objects
error: Error building trees

When looking at the .git folder, there were some weird permissions:

$ l .git/objects
...
drwxr-xr-x l-lin users 4.0 KB Mon Jun  3 13:49:35 2024 e5
drwxr-xr-x root  root  4.0 KB Mon Jun  3 15:25:38 2024 e6
...

Success

$ # fixing by changing the folder permission
$ chown -R l-lin:users .git/objects

No gui when installing hyprland ✅

I tried to follow the wiki https://wiki.hyprland.org/Nix/Hyprland-on-NixOS/ by just adding:

# configuration.nix
 
{inputs, pkgs, ...}: {
  programs.hyprland = {
    enable = true;
  };
}

but when I rebooted, there was no GUI, only a terminal…

It seems it’s not starting Hyprland be default, so I need to call it.

Not working using VirtualBox

I think my graphic card cannot handle hyprland? However, it’s still not working. Even if I enable virtualbox 3D acceleration on Settings > Display > Extended features, it’s not working.

If I use sway instead, it’s working as expected.

By using KVM with QEMU, I could make it work either using the command line:

qemu-system-x86_64 \
  -enable-kvm \
  -m 4G \
  -smp 2 \
  -hda nix-gnome.qcow2 \
  -netdev user,id=net0,net=192.168.0.0/24,dhcpstart=192.168.0.9 \
  -device virtio-net-pci,netdev=net0 \
  -device virtio-vga-gl \ # use virtio device
  -device intel-hda \
  -device hda-duplex \
  -display gtk,gl=on # enable openGL

or using virt-manager by configuring:

  • Video Model: Virtio
  • Display Spice
    • Listen Type: None
    • Check OpenGL
    • Select 0000:00:02:0 Intel

Small resolution on sway ❌

When trying to increase the resolution according to https://www.drakerossman.com/blog/wayland-on-nixos-confusion-conquest-triumph:

$ swaymsg -t get_outputs
...
Available modes:
  800x600 @ 60.xxx Hz
  640x480 @ 49.xxx Hz

There is no 1920x1080…

Tried the solution on https://superuser.com/questions/1598970/how-do-i-get-a-resolution-higher-than-1024x768-in-virtualbox by switching to VBoxSVGA, but not working…

Fail

I did not continue configuring Nix for sway. I’m now using hyprland.

Cannot execute Hyprland ✅

If I just add hyprland in home-manager, and not in NixOS, I cannot execute Hyprland. So the following configuration is still needed:

# configuration.nix
 
{inputs, pkgs, ...}: {
  programs.hyprland = {
    enable = true;
  };
}

LazyVim with home-manager ✅

I’m trying to re-configure neovim using home-manager. However, LazyVim is using a lazy-lock.json, which is updated each time a plugin is updated.

Success

Removing the lazy-lock.json from home-manager, so it’s not the latter that will handle this file. However, that means we will need to version this file somewhere else.

Firefox configuration ✅

I login into firefox and tried to sync everything. However, it seems it’s not syncing well, as there are some issue with some plugins…

Success

I forgot how the Firefox extensions worked…

Cannot install MS Teams ✅

I cannot install MS Teams with the default configuration:

{ pkgs, ... }: {
  home.packages = with pkgs; [
    teams
  ];
}

Not all LSP servers are working in Nvim ✅

I had an error when running Lua LSP:

/home/l-lin/.local/share/nvim/mason/bin/lua-language-server"	"stderr"	"Could not start dynamically linked executable: /home/l-lin/.local/share/nvim/mason/packages/lua-language-server/libexec/bin/lua-language-server\nNixOS cannot run dynamically linked executables intended for generic\nlinux environments out of the box. For more information, see:\nhttps://nix.dev/permalink/stub-ld\n

It’s related to the next section, i.e. executing a binary in NixOS, because lua-language-server is a binary that used some D

Patching the binary interpreter manually.

I managed to make lua-language-server works by patching the interpreter:

patchelf --set-interpreter $(patchelf --print-interpreter `which find`) /home/l-lin/.local/share/nvim/mason/packages/lua-language-server/libexec/bin/lua-language-server

However, it’s not quite the best approach, as I will need to perform it again once I upgrade this LSP.

Using lazy-lsp.nvim.

The lazy-lsp.nvim plugin makes the LSP servers work in NixOS. However, that means I need to update my LSP configuration, especially for Java, where I used nvim-jdtls. For now, I’m not quite ready to perform the change…

Maybe using lazy-nix-helper.nvim?

Cannot execute binaries on NixOS ✅

Some resources:

Patching the binary interpreter manually.

I managed to make lua-language-server works by patching the interpreter:

patchelf --set-interpreter $(patchelf --print-interpreter `which find`) /home/l-lin/.local/share/nvim/mason/packages/lua-language-server/libexec/bin/lua-language-server

However, it’s not quite the best approach, as I will need to perform it again once I upgrade this LSP.

I want to use openfortivpn-webview, and this one is a dynamic library that needs lots of missing libraries:

$ ldd openfortivpn-webview | grep 'not found'
        libgobject-2.0.so.0 => not found
        libglib-2.0.so.0 => not found
        libgio-2.0.so.0 => not found
        libnss3.so => not found
        libnssutil3.so => not found
        libsmime3.so => not found
        libnspr4.so => not found
        libdbus-1.so.3 => not found
        libatk-1.0.so.0 => not found
        libatk-bridge-2.0.so.0 => not found
        libcups.so.2 => not found
        libdrm.so.2 => not found
        libgtk-3.so.0 => not found
        libpango-1.0.so.0 => not found
        libcairo.so.2 => not found
        libX11.so.6 => not found
        libXcomposite.so.1 => not found
        libXdamage.so.1 => not found
        libXext.so.6 => not found
        libXfixes.so.3 => not found
        libXrandr.so.2 => not found
        libgbm.so.1 => not found
        libexpat.so.1 => not found
        libxcb.so.1 => not found
        libxkbcommon.so.0 => not found
        libasound.so.2 => not found
        libatspi.so.0 => not found

Adding the RPATH for all those libraries looks quite daunting… and I’m not quite motivated to do it.

VPN with SAML ✅

Building openfortivpn-webview with NPM not working:

$ npm run dist:linux
Error: Exit code: 127. Command failed: /home/l-lin/work/openfortivpn-webview/openfortivpn-webview-electron/node_modules/7zip-bin/linux/x64/7za a -bd -mx=9 /home/l-lin/work/openfortivpn-webview/openfortivpn-webview-electron/dist/openfortivpn-webview-1.2.0.tar.xz /run/user/1000/t-6J8Qpj/0.tar
Could not start dynamically linked executable: /home/l-lin/work/openfortivpn-webview/openfortivpn-webview-electron/node_modules/7zip-bin/linux/x64/7za
NixOS cannot run dynamically linked executables intended for generic
linux environments out of the box. For more information, see:
https://nix.dev/permalink/stub-ld

One blog post to mitigate the issue: Packaging Node Applications in Nix using Yarn2Nix or using node2nix.

Some other posts that did not work:

Finally, I managed to make it work by using nix-ld! It’s breaking a bit the nix philosophy, but at least it’s working…

Long shutdown ✅

When shutting down or restarting my computer, there’s a process that is too long to shutdown.
In the shutdown logs, there is something:

A stop job is running for user manager (0 / 2 minutes)

I thought it was k3d, so I uninstalled it, but it was not it.

Looking at journalct, I got the following:

Jun 25 08:15:30 nixos systemd[1]: Stopped Firewall.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-0c3556ff-8251-4f59-8177-6d588ee7bdd3.scope: Stopping timed out. Killing.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-0c3556ff-8251-4f59-8177-6d588ee7bdd3.scope: Killing process 3383 (zsh) with signal SIGKILL.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-83ec6d33-bd46-4a77-919d-d35bb8b21430.scope: Stopping timed out. Killing.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-83ec6d33-bd46-4a77-919d-d35bb8b21430.scope: Killing process 3420 (zsh) with signal SIGKILL.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-e9a9f708-64ea-4fea-a562-51e5ca96adbb.scope: Stopping timed out. Killing.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-e9a9f708-64ea-4fea-a562-51e5ca96adbb.scope: Killing process 3182 (zsh) with signal SIGKILL.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-3cee71a9-ecc9-4e84-bbb8-47de3195f719.scope: Stopping timed out. Killing.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-3cee71a9-ecc9-4e84-bbb8-47de3195f719.scope: Killing process 3163 (zsh) with signal SIGKILL.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-1dbe3a34-b5c8-4947-a09d-03d6b396438f.scope: Stopping timed out. Killing.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-1dbe3a34-b5c8-4947-a09d-03d6b396438f.scope: Killing process 3116 (zsh) with signal SIGKILL.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-b7291c70-9cce-4594-8b7c-1003226fc012.scope: Stopping timed out. Killing.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-b7291c70-9cce-4594-8b7c-1003226fc012.scope: Killing process 3213 (zsh) with signal SIGKILL.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-0c3556ff-8251-4f59-8177-6d588ee7bdd3.scope: Failed with result 'timeout'.
Jun 25 08:16:59 nixos systemd[1562]: Stopped tmux child pane 3383 launched by process 1798.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-0c3556ff-8251-4f59-8177-6d588ee7bdd3.scope: Consumed 4.125s CPU time.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-1dbe3a34-b5c8-4947-a09d-03d6b396438f.scope: Failed with result 'timeout'.
Jun 25 08:16:59 nixos systemd[1562]: Stopped tmux child pane 3116 launched by process 1798.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-1dbe3a34-b5c8-4947-a09d-03d6b396438f.scope: Consumed 22.726s CPU time.
Jun 25 08:16:59 nixos systemd[1562]: tmux-spawn-3cee71a9-ecc9-4e84-bbb8-47de3195f719.scope: Failed with result 'timeout'.
Jun 25 08:16:59 nixos systemd[1562]: Stopped tmux child pane 3163 launched by process 1798.
...

There are also people experiencing the same issue: https://www.reddit.com/r/linux4noobs/comments/1djgm8q/tmux_delaying_the_shutdown_process/ (posted at 2024-06-19, so maybe a recent issue?).

I tried to tmux kill-server before rebooting, and I do not have this long wait to wait until the tmux-spawn is killed.

A “hacky” mitigation is to invoke this tmux kill-server before shutting down / rebooting. It’s possible to do it with wlogout in its config file:

{
    "label" : "shutdown",
    "action" : "tmux kill-server && systemctl poweroff",
    "text" : "Shutdown",
    "keybind" : "s"
}
{
    "label" : "reboot",
    "action" : "tmux kill-server && systemctl reboot",
    "text" : "Reboot",
    "keybind" : "r"
}