Nonchalant Guidance

About Me·RSS·My Projects·LinkedIn


Added on: Wednesday, 01 November, 2023 | Updated on: Monday, 21 October, 2024

Migrating to NixOS (Part 3)

Customization to the next level

It’s been a while since I’ve posted on this series, but a lot has changed in that time. I’ve managed to learn more Nix and become more comfortable with the ecosystem, which has allowed me to make more changes to my system.

While these changes are less NixOS-specific, it seems much harder to me to be able to do and more importantly, maintain them without Nix.

This post will just be a brief list of all the changes I’ve ended up implementing. If you want to follow along at home and copy my configs, you can get my NixOS config, clone and search for the required settings.

Downloading more RAM (no really)

zram is a nifty feature which requires the sacrifice of some RAM and then using this RAM to create a compressed swap device in memory. In my current config, I have given 1 gigabyte of RAM in exchange for 20 gigabytes of zram, which helps mitigate OOM freezes, and helps speed up some big compilations.

OOM

Speaking of OOM, rather than using systemd-oomd, which didn’t seem to have much of an effect for me, I switched to earlyoom, which ended up solving the “system freezes under load” problem for me. The worst I see now is that my system becomes a bit less responsive, especially during linking.

No global programming tools

I also decided to embrace Nix dev shells and removed almost all of my development tools from my user install, instead using per-project flakes to install stuff like the Rust toolchain, C compilers, debuggers, LSPs etc. This also allowed me to configure mold as the default linker for C/C++/Rust projects, which gives me a nice speedup during linking.

In the future, I would like to have these per-project flakes serve as an easy way to sandbox my developer environments, such that as soon as I enter these directories and the flake loads, I can enter a less privileged environment. One of the flaws with the way code-sandbox does it is that it is a very rigid shell script. A Nix module that can be applied and adjusted per flake would be much better (for example, does it require IPC access? does it need Wayland or sound?)

CLI improvements

I’ve replaced some of the GNU coreutils with Rust rewrites for getting a faster, more modern equivalent. Some of these are also aliased so typing ls actually triggers eza, for example.

This includes:

I have an alias called scanfor which allows me to use sk and ripgrep to search in all text files contained in a folder recursively and open that file up in Helix. This means if I’m not sure about where I have services.syncthing defined in my Nix config repo, I can type scanfor and search for services.syncthing. A press of the Enter key will open the required file in Helix. This has served me well a lot, and its usefulness scales with the size of the project I would work on.

Similar to this, I also have the . alias, which just runs hx .. This opens up the Helix file picker at the current folder, allowing me to search by file name and open that file.

There are also some aliases which shorten nixos-rebuild commands, so I can execute them from anywhere in my filesystem and have easy commands for switching to a new NixOS config, or updating my system.

I also have my prompt configured using starship, which looks better than the bash default.

magit was also added (via an opinionated config with Vim keybindings) via an overlay. Though I don’t use magit much in daily practice, it has come in handy for speeding up some more complicated git operations.

Security enhancements

I’ve switched to using doas instead of sudo for commands that need elevated permissions. From my simple uses (and for the majority of single user Linux desktop users) doas is more than enough, and it has a much smaller codebase, with a lot less attack surface, which makes it a more secure utility.

I finally enabled the firewall on my system, and have ports open for KDE Connect, Syncthing etc.

I have a custom KeepassXC overlay (basically, an overlay is a way you can modify a Nix function) which disables X11 and networking support, so it can only run as a Wayland program and only make Unix sockets (for communication with browser extension).

I replaced the default NTP daemon on my system (systemd-timesyncd) with chrony, since chrony has support for NTS (Network Time Security), which reduces the chances that an attacker can manipulate the time on my system by spoofing NTP packets.

I also took this time to learn a bit more about systemd service hardening, and used that knowledge (and a ton of trial and error) to sandbox some services, such as Tailscale, such that they don’t have access to my private files and any permissions that they don’t really need.

Home Manager 2: Electric Boogaloo

I’ve also restarted using Home Manager for config file management. I had removed Home Manager from my config earlier since it wasn’t working properly when I moved my NixOS system config to flakes, but now I’ve added it as a NixOS module, which means that whenever I need to make a change in Home Manager, I have to rebuild my system.

I’ve ended up moving most, if not all of my configs to Home Manager, including ones for:

I’ve also been able to house my KDE Plasma config in Nix as well, thanks to the excellent plasma-manager module. This, combined with creating immutable config files for certain Plasma/KDE programs not covered by plasma-manager, has led me to have virtually my entire desktop config within Nix. I even have two Plasma plugins as git submodules within my Nix config repo.

Anything not in Home Manager is covered by Impermanence, and if some data isn’t covered by either of these, it is either non-config personal data, like pictures and personal files, or it doesn’t matter ;)

Key Remapping

I tried out keyd, which is a pretty simple program that helps you remap keys for custom shortcuts etc.

I simply swap the Caps Lock and Escape keys, and the difference is truly night and day. It took me maybe a week or two to fully train my fingers to not instinctively press the actual Escape key in Helix and instead go for the Caps Lock key.

I also have a layer that activates when Caps Lock is held, and this has let me map the h, j, k and l keys to be arrow keys. For example, Caps Lock + h is equivalent to the left arrow key. This is a gamechanger and lets me bring a little bit of Vim to every program.

ZFS

Truly “the last word in filesystems”, I decided to try out ZFS on my machine, since I was becoming more concerned about the future of btrfs, given that Red Hat dropped it from RHEL and the new kid on the block seems to be bcachefs. ZFS also has native encryption, which seemed intriguing, even if some metadata about the subvolumes (datasets in ZFS parlance) is leaked. Using native encryption seemed to be the more reliable option, since ZFS’ error correction can work with it, and there is one less layer that can bork things. NixOS is also one of the few Linux distros that has first-class ZFS support (ZFS is harder to set up on other distros due to the complicated licensing issues preventing ZFS’ inclusion in the Linux kernel, but NixOS hand waves this away for you and gives you ZFS support even in its default USB install media).

I don’t really use the advanced features of btrfs or ZFS outside of creating subvolumes and enabling zstd compression on them, so I am a poor judge of which filesystem is better. However, I feel more comfortable with a ZFS install due to the above mentioned reasons.

Testing out Reproducibility

After porting my installnix.sh script to create ZFS partitions and use native encryption instead of LUKS, I ended up reinstalling my system. Before reinstallation, I opted to only back up my /persist and select parts of my home folder. I opted not to back up any dotfile that wasn’t in /persist.

When the reinstall had been completed, I was only missing one or two things, which was solved by getting the Nix repo back on my system and rebooting (Plasma was missing the two plugins I have stored in my repo as submodules)

Repo refactoring

There were a couple refactors in my Nix config repo to help organize the code. I now try to have each service have its own file and import that separately, which makes it super easy to make a service disappear entirely from my system, simply by commenting it out. It also makes it easy to paste snippets from others’ configs or let others copy my code for their configs, as well as share some configs between different NixOS configurations. This is how my Raspberry Pi config lives off a lot of the same config as my main machine’s config.

Conclusion

I’ve ended up making quite a lot of changes in my system, and the best part is that these are stable changes; an OS update isn’t going to wipe them out, or have me tweak a new setting. Even if something like this is required, it will go inside a git repo and not an undocumented, obscure path in the traditional FHS where I will forget all about it.

I also feel far more comfortable trying out experimental software or new tools in my workflow, like keyd, on NixOS since I know I can always rollback to the old method by reverting some commits or rebooting.


This website was made using Markdown, Pandoc, and a custom program to automatically add headers and footers (including this one) to any document that’s published here.

Copyright © 2024 Saksham Mittal. All rights reserved. Unless otherwise stated, all content on this website is licensed under the CC BY-SA 4.0 International License