Rethinking Dotfiles (Nix)
After years of accumulating a messy collection of dotfiles, I'm finally ready to take the plunge into declarative configuration management with Nix home-manager.
The Problem with Chezmoi + Ansible
My current setup relies on chezmoi combined with shell scripts and Ansible. What started as a clean solution has grown increasingly complicated over time.
On openSUSE, local Ansible playbooks worked reasonably well. But after switching back to Arch, the approach completely fell apart. Ansible on Arch forces you to configure too much through raw shell commands. What was once a playbook is now a collection of shell scripts calling other shell scripts. The simplicity is gone.
Chezmoi handles the dotfiles themselves fine, but it doesn't solve the broader problem: setting up the environment around those dotfiles. Installing packages, configuring services, managing project-specific contexts. All of that still requires brittle scripting.
The 39C3 Spark
What reignited my interest in Nix was attending 39C3 and subsequently diving into the NixCon 2025 recordings on media.ccc.de. Watching talks about reproducible builds, declarative system configuration, and the matured home-manager ecosystem made me realize how far the Nix community has come since my first encounter with NixOS back in early 2020.
The tooling has improved significantly. Flakes provide a standardized way to define inputs and outputs. Home-manager has grown into a robust solution for managing user environments. The documentation and community resources are vastly better than they were back then.
Direnv: My Current Workflow
My development workflow is heavily influenced by direnv. Each project has its own .envrc that sets up the environment: language versions, tools, environment variables. It's been a game-changer for context-switching between projects.
# Example .envrc
use nix
export AWS_PROFILE=project-x
export KUBECONFIG=~/.kube/project-x.configDirenv + nix-shell already gives me per-project tooling. But managing the non-project-specific parts of my environment (shell configuration, editor setup, CLI tools) remains manual and messy.
What Nix Home-Manager Offers
Home-manager takes the declarative approach beyond development environments to your entire user configuration:
- Modular Configuration: Split your config into logical modules. A
git.nixfor version control settings, ashell.nixfor zsh/bash configuration, aneditor.nixfor Neovim or Emacs. - Project-Specific Contexts: This is where it gets interesting. Instead of scattered AWS config files and manual profile switching, I can define project-specific modules that set up everything needed for a particular client or project.
# modules/work/project-x.nix
{ config, pkgs, ... }:
{
programs.awscli = {
enable = true;
profiles.project-x = {
region = "eu-central-1";
sso_account_id = "123456789";
};
};
home.sessionVariables = {
PROJECT_X_CONFIG = "${config.home.homeDirectory}/.config/project-x";
};
}- Reproducibility: The same configuration produces the same environment on any machine. No more "it works on my laptop" for my own setup.
- Rollbacks: Made a configuration mistake? Roll back to the previous generation. This safety net makes experimentation less risky.
The Plan
I'm not planning to switch to NixOS on the host system. Arch will remain my base. But home-manager works perfectly fine on non-NixOS systems, managing just the user environment while leaving the system configuration to pacman.
The migration will be gradual:
- Start with the basics: shell configuration, common CLI tools
- Move editor configuration into home-manager
- Gradually add project-specific modules
- Eventually have a fully declarative, version-controlled user environment
Combining Direnv and Home-Manager
The beauty is that direnv and home-manager complement each other. Home-manager handles the baseline, the stuff I need everywhere. Direnv handles project-specific overrides and development shells. Together, they provide both consistency and flexibility.
Will I end up using HRT and become a Furry? I hope not!