After using Manjaro Linux for a really long time, I finally decided it was time to switch to NixOS. Manjaro has been quite good until recently. Then there were some changes because of which my GPU hardware acceleration broke. I don’t remember all the details because it has been some time since I switched over to NixOS. All I remember is that video encoding is not able to use GPU acceleration and that was a big bummer. I tried EndeavourOS and liked it. Everything was working as expected. Yet I switched to NixOS. The reasons and more follow in this post. This is one of those posts with too many technical details, so if you prefer, feel free to skip this one.


As I mentioned before, I don’t remember the exact issue I had, but it was related to the graphics card and hardware video encoding that did not work well with Manjaro. Initially it was working, but at some point after some upgrades, it stopped working. So I thought of switching distros. I have been using Manjaro for more than 5 years at this point and needed a change anyway. So I switched to EndeavourOS. Normally I don’t switch distros by completely formatting my laptop and installing a fresh distro. I first try it out on an older laptop to see how it feels.


Once I like the basics, then I install it on an external disk with my current laptop. This is to check if all my laptop’s hardware (bluetooth, GPU, etc) is working as intended. Then I play around with it to see if all the tools I need run with out much trouble. The tools could be development tools like android studio, pico scope, arduinio etc, media tools like gimp, obs, kdenlive etc, browsers with hardware acceleration and so on. Then I configure the OS the way I like it, so colors, themes, config files for various apps etc. Finally I try to use the distro as my daily driver for a month to see if I find any missing packages or issues. I browse, code, compile, publish the blog etc.


Once I am satisfied, I install the distro on my laptop’s internal disk and do the tedious process of installing all the packages all over again. Then I meticulously copy over the code and config files from the external disk to my internal disk. This is the most boring process. Let me explain how this works. So when installing the distro on an external disk, I write down all the steps I am following from vanilla install to the time I get everything working to my liking. Then I follow those same steps when installing on my internal disk.


For example this is how I would copy over the config files from old disk to new disk

cp -r .config/kitty ~/.config/
cp -r .config/eww ~/.config/
cp -r .config/hypr ~/.config/
cp -r .config/rofi ~/.config/
cp -r .config/Code\ -\ OSS ~/.config
cp -r .config/git ~/.config
cp -r .config/mimeapps.list ~/.config
cp -r .config/zsh ~/.config
cp -r .config/imv ~/.config
cp -r .config/.android ~/.config
mv ~/.zshenv ~/
mv .mozilla ~/


Then I install all the packages like this

yay -S terminus-font-ll2-td1-ttf ttf-font-awesome-5 ttf-roboto 
yay -S ttf-nerd-fonts-symbols-2048-em ttf-joypixels zsh zsh-theme-powerlevel10k
yay -S zsh-syntax-highlighting zsh-autosuggestions zsh-completions hyprland 
yay -S eww-wayland rofi mako swaylock-effects grim wob wdisplays light blueberry
yay -S  socat acpi papirus-icon-theme arc-darkest-theme-git lxappearance qt5ct 
yay -S kvantum pamac-aur kitty kodi google-chrome mpv audacious gimp ncdu ksnip
yay -S obs-studio qt6-wayland obsidian torbrowser-launcher slurp kdenlive opencv
yay -S movit sdl sox rtaudio libva-utils avidemux-qt swaybg polkit-gnome 
yay -S xdg-desktop-portal-wlr android-studio arduino code-marketplace flutter
yay -S firebase-tools-bin libva-mesa-driver pantheon-calculator ryzenadj-git
yay -S tlpui powertop ruby base-devel sublime-merge wayvnc swayidle imv shotwell
yay -S greetd-tuigreet  python-feedparser python-pip qt5-tools sshfs kdeconnect
yay -S ventoy-bin etcher-bin
pip install sendgrid


I apply some more configurations

# edit /etc/environment and make sure the file has the following
# (comment and uncomment as needed)
QT_QPA_PLATFORMTHEME=qt5ct
QT_STYLE_OVERRIDE=kvantum
#BROWSER=firefox
EDITOR=nano
VDPAU_DRIVER=radeonsi
LIBVA_DRIVER_NAME=radeonsi


# edit /etc/bluetooth/main.conf and uncomment #Experimental = false
# and change the value to true:.
Experimental = true


# edit /etc/systemd/logind.conf and enable suspend on lid closed in docked mode
HandleLidSwitchDocked=suspend

sudo ln -sf /usr/lib/systemd/system/greetd.service /etc/systemd/system/display-manager.service
# copy /etc/greetd/style.css from backup to /etc/greetd
# edit /etc/greetd/config.toml and make sure the following are set
command = "tuigreet --remember-user-session --remember --user-menu --time --issue --cmd $SHELL"


Even more configurations

sudo gpasswd -a chandanp video
sudo gpasswd -a chandanp uucp
chsh -s /usr/bin/zsh
sudo systemctl enable bluetooth
sudo systemctl start bluetooth
xdg-settings set default-web-browser google-chrome.desktop
sudo chown -R $USER /opt/flutter


# Disable coredump by creating file /etc/sysctl.d/50-coredump.conf with the
# following contents
kernel.core_pattern = /dev/null


# to disble nvidia and reduce battery usage, copy over
# /etc/udev/rules.d/00-remove-nvidia.rules from backup
# change display manager, reboot and login into hyprland
# open tor and import bookmarks by click on 
# hamburger menu > Bookmarks > Manage Bookmarks > Import and Backup > Restore

# disable firewalld so kde-connect can work
sudo systemctl stop firewalld
sudo systemctl disable --now firewalld
sudo pacman -R firewalld
# run kde connect settings and pair the devices again


nmcli connection modify Chandan_5G ipv6.method "disabled"
nmcli connection modify ChandanNet1 ipv6.method "disabled"
nmcli connection up Chandan_5G


As you can imagine, that is a lot of work every time I decide to wipe my laptop and reinstall a distro or when I switch to a new laptop. I did not like this process and was looking for better approaches and that is when I stumbled upon NixOS. Things changed quite drastically after that.


Now, I just have one canonical configuration file which is updated with all the configurations and packages I need as I am using the laptop. If I want to wipe and resintall, all I have to do is check in the canonical configurations to git, wipe my disk, fetching the configs from git or some other backup and start the install process. My laptop will be installed and configured exactly the way it was before it got wiped! It is that simple. No need for any scripts, copying and editing config files etc.


I use home manager and flakes to setup my system. Currently I have configured everything for my Acer Aspire 7 laptop, but adding more laptop configurations is easy, so I can use the same config files to install on different laptops/desktops and all my packages and configurations will be exactly the same everywhere. What does that mean? When I buy a new laptop, I don’t have to worry about the tediously configuring the system to my needs. Let me explain with some examples.


Whenever I install VS code on my freshly installed linux distro, I have to install some extensions manually. In NixOS the extensions will be installed by default when I do a fresh install because I have something like this in my nix file

# vscode related things
(vscode-with-extensions.override {
    vscodeExtensions = with vscode-extensions; [
      yzhang.markdown-all-in-one
      ms-vscode.cpptools
      streetsidesoftware.code-spell-checker
      bbenoist.nix
    ] ++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
      {
        name = "vscode-front-matter";
        publisher = "eliostruyf";
        version = "8.2.0";
        sha256 = "cA2w1Xtdqhl1TrV8ZQpQPFKblgFYlGKxXRlgueSkG3k=";
      }
      {
        name = "clipboard-history";
        publisher = "anjali";
        version = "1.0.7";
        sha256 = "Wk28DTdpAjHg378OgrWUE4HlUBeZqInZ/fN0MWU0vNU=";
      }
      {
        name = "platformio-ide";
        publisher = "platformio";
        version = "3.1.1";
        sha256 = "fwEct7Tj8bfTOLRozSZJGWoLzWRSvYz/KxcnfpO8Usg=";
      }
      {
        name = "python";
        publisher = "ms-python";
        version = "2023.3.10341119";
        sha256 = "f5vbXcqKwCnL+vsTcOX7rWUfoXNih5ZaWr3XUpCYB/M=";
      }
      {
        name = "gitlens";
        publisher = "eamodio";
        version = "2023.3.3005";
        sha256 = "X6MGpe4BH8ekNsS4AZp6YjfaU7B2x+wUjX9wc5YZCG0=";
      }
    ];
  }
)


So extensions like python, platform IO, git etc are installed by default. Likewise I can install apps by default with something like

# dev tools
git
meld
sublime-merge
android-studio
arduino
platformio
avrdude
flutter
nodePackages.firebase-tools        # firebase tools
google-cloud-sdk                   # google cloud tools
picoscope                          # for oscilloscope
jekyll                             # for blog
fritzing                           # for building electronic circuits
nodejs


I don’t have to copy over zsh config files because a simple home manager nix file will do

programs.zsh = {
  enable = true;
  enableCompletion = true;
  enableAutosuggestions = true;

  history.path = "$HOME/.config/zsh/zsh_history";
  history.expireDuplicatesFirst = true;
  history.extended = true;
  history.ignoreSpace = false;

  ...

  dotDir = ".config/zsh";
  initExtra = ''
    fpath+=${pkgs.zsh-completions}/share/zsh/site-functions
    source ${pkgs.zsh-powerlevel10k}/share/zsh-powerlevel10k/powerlevel10k.zsh-theme
    source $ZDOTDIR/p10k.zsh
    source $ZDOTDIR/completion.zsh
    ...
  '';
};


With all these config files, a fresh install of NixOS is simply a few steps like so

# fetch your flake and drop into unstable nix shell so we can use flakes
scp -r chandanp@192.168.0.77:/media/.backup/chandan/laptop/workspace/flake /mnt/
rm -rf /mnt/flake/.git
nix-shell -p nixUnstable git

# WARNING! this step is very important.
# edit /mnt/flake/hardware.nix and change the "/boot" device to 
# "/dev/disk/by-label/boot" or whatever the label is for the EFI partition. for
# dual parition it may be "ESP", for usb install it may be just "boot"

# next run the following command to install the OS
nixos-install --flake /mnt/flake#laptop-2021-acer

# reboot to new installation


The other big advantage of NixOS is the upgrades are more hermetic. So if I upgrade my system with the latest packages and something breaks, I can simply boot into the previous state (before the upgrade) and everyhting (well mostly), will be back to how it was. I used this trick a few times because I am always on unstable branch of NixOS and upgrading may sometimes break something. Then I just reboot to previous config. No need for rollbacks or uninstalls!


Moreover, my system does not acrue cruft over time as I install some packages and forget to uninstall. All the packages I am using are in the current nix file. If I remove something in it or rollback to a previous commit in git, my system is cleaned up. Well, the clean up part is not entirely true. The old packages and symlinks will be lying around, but that can be easily remedied with just a couple of commands –

sudo nix-collect-garbage --delete-older-than 30d
nix-collect-garbage --delete-older-than 30d


What can be simpler than that? I can keep going on and on, but you get the picture. Now, I am not saying it was easy. Getting up to this level, took a lot of reading and understanding and writing a bunch of config files. I probably spent 3 months just getting to know NixOS and what all the things mean. I am sure I forgot most of it by now because it was so complicated :). But I know enough to make any config changes as needed. Moreover, since the way it was designed (almost every file is a symlink on a readonly filesystem), many apps have to be hacked up make them work on it. Yet, it is still worth the effort. Thankfully, the documentation is pretty good and forums are helpful. If you are like me who frequently reinstalls their whole system, give NixOS a try. You will love it.