How do you organize your configuration?

I’m sure this is a fairly frequent question, however I haven’t seen any concrete discussion on the whys and hows of your configuration. This is primarily from my search for a solution that I enjoy using, with a primary focus on a consistent configuration across multiple machines. I can elaborate a bit on my own configuration:

  • I initially, like everyone, did everything in a single configuration.nix file, installed some packages here, configured some things there. Once I installed NixOS on my desktop machine I needed a way to have separate machine configuration but wanted my “userland” to stay the same.
  • At some point I figured out I could create machine specific configurations by having a separate configuration.nix files that I symlinked from my ~/.dotfiles repo into /etc/nixos/configuration.nix that imported configuration files/ I had something like graphical.nix that configured my Plasma/graphical apps that I’d import but keep machine specific configuration in their own configuration.nix files.
  • I discovered home-manager and integrated it into my configuration as a NixOS module, moving over my dotfiles and merging them into my system configuration. I really enjoyed this, sans finding it annoying that I had to rebuild my system to get new changes for small dotfile changes, so I just symlinked parts of the config that I changed all the time instead of building it with Nix (my Emacs configuration especially).
  • I then switched to the unstable branch for my machines with nix-channel. After having tried out niv for some projects where I needed to pin dependencies and seeing a few people try it out on their configuration I switched to using it for managing my channels and overlays. This works, but due to how I’ve configured things I have to rebuild my system twice for it to update to the latest unstable as mandated by niv… and I managed to completely break my store by not reading the documentation on what a command did and now I can’t update anymore… which leads me to this.

I’m looking at a way to restructure my configuration and dotfiles, not really sure how yet but I liked where I was conceptually heading, a pinned nixpkgs version that’s consistent across my machines. I might also try out using home-manager as a regular program and not a NixOS module due to frustrations with having to build the system on minor changes.

Anyone willing to share thoughts, experiences on how they ended up with the configuration they have now, pain points, things they want to improve etc? :slightly_smiling_face:


Mine is not that complicated. I have a git repo with all my nix configs. I clone it to /etc/nixos/nix-configs. Inside nix-configs is a nix file for each machine plus a modules directory which has any nix modules that are shared between configurations.

When I build a new machine, I just clone nix-configs and edit the configuration.nix so it does nothing but include the hardware-configuration.nix and nix-configs/machine-name.nix.

1 Like

You can get an overview on this wiki page.

A common pattern is separate folders for packages, modules, tests and system configs, for example.

1 Like

Yeah, I also struggling with organizing dotfiles. I’ve looked at the home manager, but it seems like it solves a slightly different problem.

What I ended up with is a home directory in my config repo, and a script which recursively symlinks stuff, which I run manually. Instead of, I’d love to just say “sync /home/matklad/config/home to /home/matklad” in my configuration.nix, but I don’t know how.

1 Like

mine is a single org-mode file that tangle to many small files,
I can’t even tell why, I simply start like that and left in
place… Essentially I divide my org config in headings and any
first-level heading tangle to a single file…

– Ingmar


I’ve looked at enough repos that I’m nearly cross eyed now, I’m not really looking for code but more of an discussion on why you chose to do things the way you did :slightly_smiling_face:

I actually have something for this, but you need home-manager for it. I have a execute function that I found somewhere on GitHub that you can use to run arbitrary shell expressions after activation of packages. It’s how I symlink my Emacs configuration at build-time.

1 Like

Maybe I am misunderstanding what you are trying to achieve, but symlinking stuff is one of the things home-manager does. What I like conceptually about it most is that you don’t have to manage dotffiles any more, if you don’t want to: The mess of scattered files polluting your home directory is reduced to an implementation detail of your user environment configuration.

To answer your original question: I follow the common pattern of separating machines, modules and configuration units in folders. System and user configuration are separated, since I use both Linux and Darwin, and my user environment (at least in the shell) should be portable. User configuration directories are additionally separated by abilities, such as Linux/Darwin, textual/graphical. Maybe that is a bit overkill, but at some point I felt it was was more convenient than hand-picking for each machine. Maybe I’ll revert that.

Bootstrapping NixOS is running a script in the system repo, and the system configuration has a service which bootstraps my user environment if it’s not in place - also by running a script, i.e. without having to assume much about it. That way I don’t need home-manager as a system module. That one-command bootstrapping is very nice to have. It also includes a custom variant of nixos-infect that I use for VPS.

Secrets are considered state and are imported from USB onto the file system during bootstrap.

That whole setup runs so well that I have barely touched it in months, and only for additions. I would very much like to have more control over my Mac, but haven’t had the time to look into nix-darwin yet.

My user environment can be found
here. I don’t have my systems public because of some private parts concerning shared folders and other users. Maybe I will separate that out at some point, but probably not.

1 Like

Which sounds great in principle, but ends up being very annoying in practice, because even the slightest change to any of your home-manager-managed dotfiles requires a home-manager switch. Typo in your lastest config? home-manager switch! Not quite the setting you wanted? home-manager switch!

At least that’s my experience with using the home.file.".foo".source = /my/config/repo/foo; feature. Do you have have a more convenient solution, that allows experimenting with dotfile tweaks without having to home-manager switch on every single iteration?

Well, that is how nix works in general. If you just want to reduce the amount of interaction while twiddling with your configs, you can execute home-manager switch on file system changes with something like entr.

A possibility is to change

home.file.".foo".source = /my/config/repo/foo;


home.file.".foo".source = config.lib.file.mkOutOfStoreSymlink /my/config/repo/foo;

and do a home-manager switch. Then you can hack away on the foo file in-place.

When you are happy with the file then remove the config.lib.file.mkOutOfStoreSymlink and do another home-manager switch to go back to a declarative configuration.


Great! Tracking down a solution like this (or hacking something inferior myself) has been on my mind for some time, so thank you for pointing this out.

What are the downsides of keeping mkOutOfStoreSymlink in my configuration, as opposed to switching in and out when tweaking with the configuration, given that all these files being linked in this way live in the same git repo as my home.nix itself?

The obvious one I can think of is that, now that they’ve been taken out of read-only file store, something might modify them behind my back, temporarily. Anything else to be aware of?

BTW, thank you very much for creating home-manager.

1 Like

When using mkOutOfStoreSymlink you effectively make your configuration non-reproducible. Like you say, this means that the content of a file within a known working generation may change. If you are unlucky and mess up that file then you can’t simply re-activate that working generation to restore it.

This may not be a large problem if you are keeping your out of store files in some form of source control but it does complicate things a bit if you want to restore your configuration to an earlier state.

I would in general advice to use mkOutOfStoreSymlink sparingly and ideally only temporarily.