I recently decided to install NixOS on my laptop.
Why
I’ve been running Debian for a while, mainly for the stability. I am a college student and cannot afford to break my install after messing up a configuration.
The primary reason for the switch is because I was interested in making my whole setup declaritive. More recent packages were a nice bonus.
Configuration
NixOS is configured by two files: configuration.nix and hardware-configuration.nix.
configuration.nix is filled with all options that makes your install, hardware-configuration.nix should not be changed as it was automatically generated by the NixOS installer.
I split up my configuration into multiple files and directories.
nixos (main) -> tree
.
├── configuration.nix
├── desktop.nix
├── hardware-configuration.nix
├── packages
│ ├── core.nix
│ ├── desktop.nix
│ └── school.nix
├── system
│ ├── audio.nix
│ ├── boot.nix
│ ├── locale.nix
│ └── networking.nix
└── users.nix
Now my configuration (and packages) are organised based on how I use them.
Flakes
If you read one of my previous posts My custom tool that makes college easier you know that I made a productivity script for my college workflow.
This script worked just fine after switching to NixOS, just like the scripts I made for my Eww bar.
After learning about flakes I couldn’t wait to make all of these scripts declaritive.
setup-for-college (main) -> tree
.
├── flake.lock
├── flake.nix
├── readme.md
└── setup-for-college.sh
Check out the source code on Github.
{
description = "tool that makes it easier to manage semesters and courses";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
outputs = { self, nixpkgs }: {
defaultPackage.x86_64-linux = self.packages.x86_64-linux.setup-for-college;
packages.x86_64-linux.setup-for-college =
let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
name = "setup-for-college";
scriptContent = builtins.readFile (self + "/setup-for-college.sh");
script = pkgs.writeShellScriptBin name scriptContent;
buildInputs = with pkgs; [ ];
in pkgs.symlinkJoin {
name = name;
paths = [ script ] ++ buildInputs;
buildInputs = [ pkgs.makeWrapper ];
postBuild = "wrapProgram $out/bin/${name} --prefix PATH : $out/bin";
};
};
}
You can specify dependencies via the buildInputs list. This makes sure that the required packages are installed before running the script.
This snippet shows you the buildInputs for my bar-modules script. It requires networkmanager (to use nmcli), alsa-utils (to use amixer) and bspwm (to use bspc).
buildInputs = with pkgs; [ networkmanager alsa-utils bspwm ];
Check out the source code on Github.
I completely rewrote my logic for the modules on my bar. Before the refactor it consisted of completely seperate scripts. I made functions for each module and merged them via one logic.sh script.
Conclusion
Overall I am enjoying the change of mindset (imperative to declaritive), thanks for reading this post!