Modules to combine your NixOS, home-manager and nixpkgs configurations. This provides a means for people to get started with configuring flake-based Nix configs without needing to dive deep into understanding how Flakes work while also providing power users with options to configure multiple hosts in a simple and maximally configurable manner.
This uses flake-parts. You don't have to (and I built it without any explicit dependencies on it) but I leave it as an exercise to the reader to figure out how to use this otherwise.
A sample config may look like:
{
description = "My Nix system configuration with nix-config-modules";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
nix-config-modules.url = "github:chadac/nix-config-modules";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
emacs-overlay.url = "github:nix-community/emacs-overlay";
};
outputs = { flake-parts, ... }@inputs: flake-parts.lib.mkFlake { inherit inputs; } {
# import nix-config-modules
imports = [ inputs.nix-config-modules.flakeModule ];
# this avoids errors when running `nix flake show`
systems = [ ];
nix-config = {
# Tags are described below in more detail: You can use these as an
# alternative to enabling/disabling applications.
defaultTags = {
# by default we will not install packages tagged with "development"
development = false;
};
# Unlike regular Nix, you can bundle nixpkgs/NixOS/home-manager logic together
apps.emacs = {
tags = [ "development" ];
nixpkgs.params.overlays = [
inputs.emacs-overlay.overlay
];
nixos = {
services.emacs.enable = true;
};
home = { pkgs, ... }: {
programs.emacs = {
enable = true;
package = pkgs.emacs-unstable;
};
};
};
hosts.my-host = {
# host types can be "nixos" and "home-manager"
# "nixos" is for systems that build NixOS; home-manager is bundled with it
# "home-manager" is for systems that install just HM (for example, darwin etc)
kind = "nixos";
# defines the system that your host runs on
system = "x86_64-linux";
# on single user systems you can specify your username straight up.
# multi-user support is "upcoming"
username = "chadac";
# optional metadata... useful for stuff like Git
email = "chad@cacrawford.org";
# you can customize your home directory, otherwise defaults to
# `/home/<username>`
homeDirectory = "/home/chadac";
tags = {
# now we tell Nix that our host needs any apps marked as
# 'development'. This enables simplified host configurations
# while also empowering users to still fully customize hosts
# when needed.
development = true;
# this is a predifined tag; for apps that are automatically
# included and optionally enabled, see <modules/apps>
getting-started = true;
};
};
};
};
}
This will create a basic Flake with some predefined defaults for a single-user NixOS system with home-manager enabled. You may then build/switch to the system using:
nixos-rebuild switch --flake .#my-host
Or with home-manager:
home-manager switch --flake .#my-host
Since this is layered with flake-parts
you may then choose to
manage/deploy your hosts however you wish.
For a more in-depth example of how this can be used, see github:chadac/dotfiles.
If you have NixOS/home-manager configurations specified in a standard
configuration.nix
or home.nix
, you can import them without needing
to refactor anything. This enables you to onboard to using Flakes for
your configuration.
To import existing configurations to a single host:
outputs = { flake-parts, ... }@inputs: flake-parts.lib.mkFlake { inherit inputs; } {
imports = [ inputs.nix-config-modules.flakeModule ];
systems = [ ];
nix-config = {
hosts.my-host = {
kind = "...";
system = "...";
# each host can specify custom nixos/home/nixpkgs attributes to customize
# their own configuration
nixos = {
imports = [ ./configuration.nix ];
};
home = {
imports = [ ./home.nix ];
};
# OPTIONAL: you can disable any apps that automatically add configurations.
# See <modules/apps> for details on what each of these do.
defaultTags = {
defaults = false;
single-user = false;
home-manager = false;
};
};
};
To import existing configurations globally:
outputs = { flake-parts, ... }@inputs: flake-parts.lib.mkFlake { inherit inputs; } {
imports = [ inputs.nix-config-modules.flakeModule ];
systems = [ ];
nix-config = {
modules = {
nixos = [ ./configuration.nix ];
home-manager = [ ./home.nix ];
};
hosts.my-host = {
# you will still need to specify `system` and `kind`
kind = "...";
system = "...";
};
};
This is primarily an example in making it really easy to build flakes that can do multi-host configurations while keeping stuff simple and readable. There are a couple annoyances I've had in the past when configuring systems with Nix:
So many flake-based Nix configs are hard to read as they're not really utilizing the module system -- this is much simpler (see the "Getting Started" example below).
Many core services require special configurations both in
home-manager
and in NixOS, or even custom configurations when
importing nixpkgs
.
Usually this is managed by throwing configurations for the same app into separate files, which can be a bit unreadable.
In nix-config-modules
, the configuration for a single application
happens all in one place -- so you can now do:
nix-config.apps.thunar = {
home = { pkgs, ... }: {
home.packages = [ pkgs.xfce.thunar ];
};
nixos = { pkgs, ... }: {
programs.thunar = {
enable = true;
plugins = with pkgs.xfce; [
thunar-archive-plugin
];
};
};
nixpkgs = {
packages.unfree = [
# just for the example... assume it's unfree
"xfce"
];
};
}
No more need to manage configurations for two packages in separate places.
Unlike regular home-manager and NixOS, the types of hosts that I manage tend to be fairly similar. The usual approach of customizing something like:
# host 1
xsession.windowManager.i3.enable = true;
xsession.windowManager.sway.enable = false;
# host 2
xsession.windowManager.i3.enable = false;
xsession.windowManager.sway.enable = true;
is pretty annoying when 99% of the time, the rest of the
configurations will remain the same. To simplify host configuration,
nix-config-modules
has a tag-based app management system as
well. This means that you can use
my-host.tags.wayland = true;
to conditionally configure which apps are deployed to each system.
NOTE: This is an optional feature. If enable
is specified (either
enable = true
or enable = false
), then that overrides tag behavior.
For example, a host can similarly disable emacs
with:
hosts.odin = {
nix-config = {
apps.emacs.enable = false;
};
};
Apps are basic configurations that combine NixOS, home-manager and nixpkgs configurations. A basic example might be:
nix-config.apps.i3 = {
tags = [ "desktop" ];
nixos = {
services.xserver = {
displayManager.defaultSession = "none+i3";
windowManager.i3.enable = true;
};
};
home = { pkgs, ... }: {
xsession.windowManager.i3.enable = true;
home.packages = with pkgs; [ dmenu i3status ];
};
};
You may also use this to customize the import of nixpkgs
; for
example:
{ inputs, ... }: {
nix-config.apps.emacs = {
tags = [ "development" ];
home = <...>;
nixpkgs = {
overlays = [ inputs.emacs-overlay.overlays.default ];
}
};
}
And of course, host tags may not only customize the enabling/disabling of apps but also their configuration:
nixpkgs = { lib, host, ... }: {
overlays = lib.mkIf host.tags.bleeding-edge [ inputs.emacs-overlay.overlays.default ];
};
Sometimes we'd like to have some apps automatically installed to our
user's system. For that, use the trait homeApps
:
homeApps = [{
tags = [ "entertainment" ];
packages = [ "vlc" "spotify" "tidal-hifi" ];
}]
This creates the following equivalent configuration:
apps.vlc = {
tags = [ "entertainment" ];
home = { pkgs, ... }: {
home.packages = [ pkgs.vlc ];
};
};
# < .. repeat for spotify, tidal-hifi .. >
It's also possible to manage NixOS/Home Manager/nixpkgs configurations on a per-host basis:
nix-config.hosts.my-host = {
# <...>
nixos = {
imports = [ ./my-host/hardware-configuration.nix ];
};
};
meh
Technically you can disable home-manager with
nix-config.defaultTags.home-manager = lib.mkForce false;
That'll remove any NixOS integrations to make HM work.
yeah I know
yeah I know
yeah I know
yeah I know
ok I didn't know that