Skip to content

Commit

Permalink
2024-08-21/01: add "An unordered list of hidden gems inside NixOS" as…
Browse files Browse the repository at this point in the history
… draft
  • Loading branch information
thiagokokada committed Aug 21, 2024
1 parent a0d421c commit 9870b17
Showing 1 changed file with 165 additions and 0 deletions.
165 changes: 165 additions & 0 deletions 2024-08-21/.01-an-unordered-list-of-hidden-gems-inside-nixos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# An unordered list of hidden gems inside NixOS

After using [NixOS](https://nixos.org/) for the last 5+ years as my main OS, I
end up with a [configuration](https://github.com/thiagokokada/nix-configs/)
with many things that are interesting for one reason or another, but it is not
listed anywhere (well, except if you are the kind of person that reads `man 5
configuration.nix` or the release notes in every release).

So kind in the same spirit as my [list of things that I miss in
Go](/2024-08-17/01-an-unordered-list-of-things-i-miss-in-go.md), here is a list
of modules that I find neat in NixOS and are not default already. Again, the
list is unordered since this makes it easier to update in the future if I find
something else, but also I don't want to think too hard about an order here.

With all above, let's start.

##
[`networking.nftables`](https://github.com/NixOS/nixpkgs/blob/6afb255d976f85f3359e4929abd6f5149c323a02/nixos/modules/services/networking/nftables.nix)

[nftables](https://www.nftables.org/) is, accordingly to Wikipedia:

> nftables is a subsystem of the Linux kernel providing filtering and
> classification of network packets/datagrams/frames.
It is basically a replacement of the venerable
[iptables](https://en.wikipedia.org/wiki/Iptables), that still exist and is the
default program to configure the famous `networking.firewall`, the declarative
[Firewall](https://wiki.nixos.org/wiki/Firewall) that NixOS enable by default.

To enable, it is simple, just add to your configuration:

```nix
{
networking.nftables.enable = true;
}
```

And thanks to the fact that NixOS's Firewall is declarative, everything should
still work as expect: any service that you set `openFirewall = true` will still
have its ports open, if you set `networking.firewall.allowPing = false` it will
still disable pings like before, etc.

If you look at the documentation of the above option, you will find the
following warning:

> Note that if you have Docker enabled you will not be able to use nftables
> without intervention. Docker uses iptables internally to setup NAT for
> containers. This module disables the ip_tables kernel module, however Docker
> automatically loads the module. Please see
> https://github.com/NixOS/nixpkgs/issues/24318#issuecomment-289216273 for
> more information.
I don't use Docker (switched to Podman instead for quite a long time), so I
don't know how bad the situation is. Also keep in mind that `nftables` does
offer `iptables-compat` for compatibility with old iptables scripts, so it is
most likely Docker doing something weird here.

Now, the actual advantage from the user here is not clear: the main advantage
from my point of view (and the reason I used to use in other distros like Arch)
is the improved syntax, however if you are using the declarative NixOS's
Firewall you are not interacting with either `iptables` or `nftables` directly
anyway. `nftables` is supposed to be more efficient, but not sure most users
will care about this.

However if you are the kind of person that needs custom rules, switching to
`nftables` does bring a few benefits, including
`networking.nftables.checkRuleset` (enabled by default), that checks if your
ruleset has syntax errors during build time. Really valuable to avoid issues
only after switch.

Anyway, this is one of those options that I think it should be the default for
a long time, since most of the new development in NixOS firewall seems to be
focusing `nftables` for a while.

## [`system.switch.enableNg`](https://github.com/NixOS/nixpkgs/blob/877d19523edcac81b167e8fd716ad2658da2adca/nixos/modules/system/activation/switchable-system.nix#L30-L38)

[This one](https://github.com/NixOS/nixpkgs/pull/308801) I just discovered
today, but it has been available for a while (~2 months if you're using
`nixos-unstable`). Finally someone is rewriting
[`switch-to-configuration.pl`](https://github.com/NixOS/nixpkgs/blob/b1eff03c35aa7c90ab3a4d9f6ef297dae5fba37b/nixos/modules/system/activation/switch-to-configuration.pl),
the Perl script that is called everytime you run `nixos-rebuild switch`.

Now, I am not one of those "rewrite in Rust" zealots, but in this case this is
definitely worth it: `switch-to-configuration.pl` is one of those pieces of
code in NixOS that most people avoid touching at the fear of breaking
something. There is a reason why
[`nixos-rebuild`](https://github.com/NixOS/nixpkgs/commit/eeb2588a59c938042b74183ce1da7052a6ef7e59)
is as convoluted as it is, because even if it is a messy shell script, most
people preferred to workaround issues from the `switch-to-configuration.pl`
inside it than trying to understand the mess that `switch-to-configuration.pl`
is.

Trying this one is easy:

```nix
{
system.switch = {
enable = false;
enableNg = true;
};
}
```

Yes, you need to explicit set `system.switch.enable = false`, since the default
is `true`.

By the way, what is the reason you would want to set `system.switch.enable =
false` before the `enableNg` appeared you ask? For systems that are immutable
and updated by e.g.: image upgrades instead of modifying root.

Enabling `switch-to-configuration-ng` right now is mostly for testing purposes,
but one of the advantages that I saw is that system switches are (slightly)
faster:

```
$ hyperfine "sudo nixos-rebuild switch" # switch-to-configuration.pl
Benchmark 1: sudo nixos-rebuild switch
Time (mean ± σ): 3.576 s ± 0.035 s [User: 0.004 s, System: 0.014 s]
Range (min … max): 3.522 s … 3.645 s 10 runs
$ hyperfine "sudo nixos-rebuild switch" # switch-to-configuration-ng
Benchmark 1: sudo nixos-rebuild switch
Time (mean ± σ): 3.394 s ± 0.080 s [User: 0.004 s, System: 0.013 s]
Range (min … max): 3.325 s … 3.608 s 10 runs
```

But yes, the difference is not enough to make a significant impact. The real
reason for the rewrite is to make it easier to colaborate. I hope one day we
also have someone brave enough to rewrite the `nixos-rebuild` script in
something saner.

## [boot.initrd.systemd](https://github.com/NixOS/nixpkgs/blob/cce9aef6fd8f010d288d685b9d2a38f3b6ac47e9/nixos/modules/system/boot/systemd/initrd.nix)

A quick recap on how a modern Linux distro generally boots: the first thing
that the bootloader (say [GRUB](https://www.gnu.org/software/grub/) or
[systemd-boot](https://systemd.io/BOOT/)) loads is `initrd` (_initial
ramdisk_), a small image that runs from RAM and includes the Linux kernel and
some utilities that are responsible for setting up the main system. For
example, one of the responsabilities of the `initrd` is to mount the disks and
start init system (`systemd`).

It may surprising that this `initrd` image does **not** generally include
`systemd`. Traditionally `initrd` is composed by a bunch of shell scripts and a
minimal runtime (e.g.: [busybox](https://www.busybox.net/)), however `systemd`
can also do this job since a long time ago. It is just the paper of the distros
to integrate `systemd` inside the `initrd`.

This is what `boot.initrd.systemd` does: enable `systemd` inside the `initrd`.
It make a few subtle changes:

- If you are using [Full Disk Encryption via
LUKS](https://wiki.nixos.org/wiki/Full_Disk_Encryption), you will get a
different password prompt at login
- You will get `initrd` time information if using `systemd-analyze` to measure
boot time
+ You can get even more information (bootloader) if you also use
`systemd-boot`
- You will also get `systemd` style status about services during `initrd` (not
only afterwards)

But I think the main reason is that since `systemd` is event-driven, it should
make boot more reliable, especially in challenging situations (like booting
from network). I can't say that I have any system like this to test if it is
actually more reliable or not, but I don't remember having any issues since I
set `boot.initrd.systemd.enable = true`.

0 comments on commit 9870b17

Please sign in to comment.