NixOS for the confused – Part I

Enough teasing! Let’s get to it. Today, we’ll create a very barebones flake that will do two things:

  1. Handle our main configuration.nix ;

  2. Install and handle our home-manager stuff.

I know you don’t care too much about these technicalities, but some context is needed. What in tarnation is configuration.nix and/or home-manager? Well, dear reader, let’s explore!

The numero uno way of configuring NixOS is via the file configuration.nix. The default location for this file is:

/etc/nixos/configuration.nix

But! It doesn’t have to be this one. This is just the default. Depending on how you installed NixOS, this file will be different from the examples we’re using here, and that’s a-ok. Long story short, configuration.nix is how you change the state of the Operating System as a whole. Kinda like rc.conf on FreeBSD. Stuff there applies to every user. Ideally, this file should be pretty simple: the vast majority of the stuff we want will be handled by Home Manager on a per-user basis.

Stuff like the bootloader, kernel modules, default shell, hostname, ssh, local firewall, etc, will go there. It is, as the name implies, written in the Nix language. Again, you don’t have to worry about that kind of jazz: we’ll start with a fresh copy, and work our way to something usable without learning a damn thing about the Nix language!

Right. What about home-manager? Home Manager is one way of dealing with user-specific stuff. Operative word here is one. There are many ways of doing this, but we’re going to use home-manager. I know there are alternatives. Don’t @ me. In a busy system, every user would have their own home-manager config, but we’re not that fancy: we will create one for you, and we can pretty much infer how to add more as we go.

Home Manager will handle our shell aliases, terminal configs, git configs, Neovim options, etc. You know, our stuff. Not system-wide shit.

Sounds good? Great. Our flakies directory is going to look something like this for now:

.
├── flake.lock
├── flake.nix
├── home-manager
│   └── home.nix
└── nixos
    ├── configuration.nix
    └── hardware-configuration.nix

The home-manager directory will hold our Home Manager stuff, the nixos directory will hold our configuration.nix, and the flake.nix file will tell NixOS how to handle both. The flake.lock file will be generated automatically. It is there to keep track of versions and things of that nature. The idea is that, given the same flake.nix and the same flake.lock, the exact same output (packages, configs, etc) will be produced. If you grab the flake.nix and the flake.lock from computer A, move it to computer B, and build it, the resulting system should be the same. That is exactly why keeping this stuff on a git repo makes sense: You install a fresh NixOS, git pull the files, rebuild everything, and boom. No more weird shell scripts, installing shit by hand, ending up with one version of software X here, and another version over there, you know... exactly what we are trying to avoid. One caveat, though: the hardware-configuration.nixfile is indeed dependent on the hardware. A hardware-configuration.nixfrom your laptop probably won’t work on your desktop. We’ll get to that in a minute, though.

Enough talk! Let’s get crackin’.

Here’s our plan for Part I:

Log into your NixOS, open that terminal, create the flakiesdirectory (hint mkdir flakies).

cd into your flakiesdirectory, create the home-manager and nixos directories:

~/flakies $ mkdir nixos home-manager

Open your favorite editor (Neovim, obviously), and create a file named flake.nix

~/flakies $ nvim flake.nix

Our initial flake.nixwill look like this:

{
 description = "My Flakie"; #You can change this to whatever

 inputs = {
   # Nixpkgs
   nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

   # Home manager
   home-manager.url = "github:nix-community/home-manager";
   home-manager.inputs.nixpkgs.follows = "nixpkgs";

   # Hardware
   hardware.url = "github:nixos/nixos-hardware";
 };

 outputs = { self, nixpkgs, home-manager, ... }@inputs: {
   # NixOS configuration entrypoint
   # Available through 'nixos-rebuild --flake .#your-hostname'

   nixosConfigurations = {
     # FIXME replace with your hostname
     FIXME = nixpkgs.lib.nixosSystem {
       specialArgs = { inherit inputs; }; # Pass flake inputs to our config
       # > Our main nixos configuration file <
       modules = [ 
         ./nixos/configuration.nix
       ];
     };
   };

   # home-manager configuration entrypoint
   # Available through 'home-manager --flake .#your-username@your-hostname'
   homeConfigurations = {
     # FIXME replace with your username@hostname
     "user@FIXME" = home-manager.lib.homeManagerConfiguration {
       pkgs = nixpkgs.legacyPackages.x86_64-linux; # Home-manager requires 'pkgs' instance
       extraSpecialArgs = { inherit inputs; }; # Pass flake inputs to our config
       # > Our main home-manager configuration file <
       modules = [ 
           ./home-manager/home.nix 
           ];
     };
   };
 };
}

Cool! Just do a couple of things here:

luffy = nixpkgs.lib.nixosSystem (...)
 "monkeyd@luffy" = home-manager.lib.homeManagerConfiguration (...) 

Good job! You are doing great!

Notice that we are referencing some files that do not exist yet, so let's get to that.

For now, let’s just copy the default configuration.nix and hardware-configuration.nixto our ./flakies/nixos directory.

While in the flakies directory, cd nixos

sudo cp /etc/nixos/configuration.nix .

sudo cp /etc/nixos/hardware-configuration.nix .

You should have configuration.nix and hardware-configuration.nix in your flakies/nixos directory, just like that tree view up there.

Edit the flakies/nixos/configuration.nix file and add the following:

# Enable Flakes
  nix.package = pkgs.nixFlakes;
  nix.extraOptions = ''
    experimental-features = nix-command flakes
    '';

While we're at it, let's make sure zsh is the shell of the land. You should have a section there for your user. Let's make it look like this for our user, Monkey D Luffy:

users.users.monkeyd = {
    isNormalUser = true;
    description = "Monkey D Luffy";
    extraGroups = [ "networkmanager" "wheel" ];
    packages = with pkgs; [];
    shell = pkgs.zsh;
  };
programs.zsh.enable = true;

Doesn't really matter where, but let's add this to the end of our flakies/nixos/configuration.nix. This will enable flakes for ya.

Now it is time to whip up a barebones home.nix. This will go under the flakies/home-manager/ directory.

$ nvim ./home-manager/home.nix

Should look like this:

{ config, pkgs, inputs, lib, ... }:

{
  home.username = "monkeyd";
  home.homeDirectory = "/home/monkeyd";
  home.stateVersion = "22.11";
  nixpkgs = {
		config = {
			allowUnfree = true;
			allowUnfreePredicate = (_: true);
		};
	};

  # Let Home Manager install and manage itself.
  programs.home-manager.enable = true;
  home.packages = with pkgs; [
        bat
        fzf
        ripgrep
        jq
        tree
        exa
	];
  
  programs.neovim = {
  	enable = true;
  	viAlias = true;
	vimAlias = true;
  	};

	home.sessionVariables = {
		EDITOR="nvim";
	};
	home.shellAliases = {
		l = "exa";
		ls = "exa";
		cat = "bat";
	};

	programs.zsh = {
		enable = true;
	};

	programs.zsh.oh-my-zsh= {
		enable = true;
		plugins = ["git" "python" "docker" "fzf"];
		theme = "dpoggi";
	};

}

Here we are adding some spice to the proceedings. We bootstrap home-manager, allow non-free software, declare some packages (bat, fzf, ripgrep, jq, tree, exa) to be installed, enable Neovim with some aliases so we can open the editor by calling it vi, vim, or nvim, create some shell aliases, enable zsh and oh-my-zsh with some plugins (git, python, docker, fzf) and a theme (dpoggi).

That was a lot. Let's build this thing now! But wait! We don't even have home-manager installed! How can we use home-manager without home-manager? We need to bootstrap this thing:

$ export NIX_CONFIG="experimental-features = nix-command flakes"

$ nix shell nixpkgs#home-manager

Do not leave this shell with home-manager. This is a temporary shell where home-manager is available. If you exit this shell, home-manager will be gone, and you'll have to nix shell nixpkgs#home-manager again. We should be good to go now:

Again, while in the flakies directory (not flakies/nixos, not flakies/home-manager, just flakies), build the system:

$ sudo nixos-rebuild switch --flake .#hostname – change hostname to whatever you picked in your flake.nix. Our example would be:

sudo nixos-rebuild switch --flake .#luffy

If everything builds with no issues, we can build the home-manager part:

home-manager switch --flake .#username@hostname – Using our example:

home-manager switch --flake .#monkeyd@luffy

Pray for the gods old and new, and if everything goes well, you should have a brand new system build from Flakes. Reboot this mess, and we'll see you on the next one!

P.S. Huge shoutout to /u/Ultra980 for proofreading this bitch!

Discuss...