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:
Handle our main
configuration.nix
;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.nix
file is indeed dependent on the hardware. A hardware-configuration.nix
from 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:
- Write a simple flake.nix
- Generate the flake.lock
- Generate (or copy) the
configuration.nix
andhardware-configuration.nix
- Write a simple
home.nix
- Bootstrap the whole thing, and rebuild the system.
Log into your NixOS, open that terminal, create the flakies
directory (hint mkdir flakies
).
cd into your flakies
directory, 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.nix
will 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:
- Change the
FIXME
to your hostname (it doesn’t have to be the hostname, but it’s a good way to organize things). If your hostname is, say,luffy
, that line should be:
luffy = nixpkgs.lib.nixosSystem (...)
- Change the other
FIXME
to yourusername@host
. If your username ismonkeyd
, that line should look like:
"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.nix
to 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:
- Let’s enable Flakes by hand. We just need to do this once:
$ export NIX_CONFIG="experimental-features = nix-command flakes"
- Let's get a shell with home-manager. Make sure you are in the
flakies
directory and run:
$ 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!