-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2024-08-21/01: add "An unordered list of hidden gems inside NixOS" as…
… draft
- Loading branch information
1 parent
a0d421c
commit 9870b17
Showing
1 changed file
with
165 additions
and
0 deletions.
There are no files selected for viewing
165 changes: 165 additions & 0 deletions
165
2024-08-21/.01-an-unordered-list-of-hidden-gems-inside-nixos.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`. |