Managing my dotfiles with Nix
There have been more than a few cases where people suggest various tools to manage my dotfiles for me. Nix is one of the more recent ones and it intrigued me after learning a bit more about how it approaches handling its packages. See, everything in Nix is built to be declarative and immutable by design, all the way down to the libraries backing the tools you would use in your day-to-day work. This fancy trick unlocks a lot of really powerful features for you. Now, I’m no expert in Nix and instead this is going to be more a journey that I’ll be documenting here as I learn more about Nix. A coworker of mine told me that when they were learning how to use Nix they started small and familiar with their dotfiles and moved onto other things from there. I’ll be doing exactly that since I can think of nothing more personal and familiar to me than my dotfiles.
Home-manager⌗
So in my exploring of what Nix is I found a tool that explicitly is for managing dotfiles called Home-manager. Installing this tool and running through the Nix Pills to start learning how to write my own Nix files really started to open my eyes to the power of how Nix manages files. My current dotfiles git repo structure is meant to be an overlay on an existing home directory. It’s installation is almost intentionally messy, but a familiar kind of messy because when I say overlay, it isn’t the nice way that docker layers overlay on each other in the overlay2 filesystem. The installation script iterates over every file in the dotfile repo and creates a symlink from your home directory to the repo itself. Sometimes clobbering your existing configurations in the process.
Now how home-manager handles installation is fascinating. Remember how I said
that Nix is declarative? Well when you write your configuration for your
dotfiles, you are not writing a .vimrc
file or a .bashrc
file. Nix expects
you to define your configurations in Nix and it will build out a .vimrc
and
.bashrc
for you. Each configuration file generated based on what is know in
Nix as a derivation or builder. Since everything in Nix is built to be immutable, Nix
keeps every version of your generated dotfiles in /nix/store
and symlinks to
the versions you actually want to use. This makes rolling back to a previous
version of your configurations super simple as its just moving some symlinks
around. Home-manager acts as a profile manager for these generated configuration
files. It sees what you have declared in your own home.nix
file and makes sure
that what you want exists in your version of home.nix
. It then modifies your
PATH
or symlinks config files into place in your home directory (if it can do
so without destroying anything) and you have a configured environment.
Dotfiles are not the only thing that Home-manager knows how to handle. It can
also handle packages and with that you can also get the same benefits of Nix’s
declarative and immutable powers. Lets take git for instance which I have
defined in my home.nix
file.
{ config, pkgs, ... }:
{
# ...
home.packages = {
pkgs.git
}
# ...
}
A version of the package git
is downloaded, built, and stored within
/nix/store
with a hash, sometimes referencing dependent libraries by their
unique hashes within the derivation for git
. git
is then symlinked into your
PATH
so that specific version is made available to you. Not only does this
allow you to ensure that the same version of a package exists for you down to
the libraries that package is dependant on, it also allows you to have different
versions of the same package installed simultaneously which other package
managers are definitely not good at handling. This may not be a big deal for
most, but for developers out there who bounce between multiple projects, this is
huge.
Anyhow, there is a lot more to explore here and I’m only at my dotfiles and am getting some use out of it so far. Needless to say, I’m excited to see where this path takes me.