Introduction
nixos-unified is a flake-parts module to unify NixOS + nix-darwin + home-manager configuration in a single flake, while providing a consistent interface at DX and UX level.
Why?
nixos-unified provides the following features:
- One-click activation & deployment
- Activation: An
.#activate
flake app that works uniformly on NixOS, nix-darwin and home-manager..#activate
can also remotely activate machines (be it macOS or NixOS) over SSH, thus acting as a simple alternative to deployment tools likedeploy-rs
andcolmena
.
- Also: an
.#update
flake app to update the primary inputs (which can be overriden)
- Activation: An
- Seamless access to top-level flake
- All NixOS/ nix-darwin/ home-manager modules receive
specialArgs
which includes all the information in the top-level flake.- This enables those modules to be aware of the flake inputs, for instance.
- All NixOS/ nix-darwin/ home-manager modules receive
- Sensible defaults
- Sensible defaults for home-manager/ nix-darwin/ and NixOS configurations (#75).
- Autowiring of flake outputs
- Autowiring: An optional module that will scan the directory structure and wire up the appropriate flake outputs automatically without you having to do it manually.
Getting Started
See: Getting Started.
Getting Started
Pick your desired operating system and follow the below instructions.
tip
Checkout nixos-unified-template for the quickest way to get started.
NixOS
- Install NixOS w/ Flakes enabled
- Convert your
flake.nix
to usingnixos-unified
using the NixOS only template as reference.
non-NixOS Linux
If you use other Linux distros like Ubuntu, you may use just home-manager
.
- Install Nix
- Use the HOME only template
macOS
- Install Nix
- Use the macOS only template
Activation
nixos-unified
provides an .#activate
flake app that can be used in place of nixos-rebuild switch
(if using NixOS),darwin-rebuild switch
(if using nix-darwin
) or home-manager switch
(if using home-manager)
In addition, it can remotely activate the system over SSH (see further below).
Activating NixOS or nix-darwin configurations
In order to activate a system configuration for the current host ($HOSTNAME
), run:
nix run .#activate
tip
Usually, you’d make this your default package, so as to be able to use nix run
. In flake.nix
:
# In perSystem
{
packages.default = self'.packages.activate
}
Activating home configuration
If you are on a non-NixOS Linux (or on macOS but you do not use nix-darwin), you will have a home-manager configuration. Suppose, you have it stored in legacyPackages.homeConfigurations."myuser"
(where myuser
matches $USER
), you can activate that by running:
nix run .#activate $USER@
note
The activate app will activate the home-manager configuration if the argument contains a @
(separating user and the optional hostname). The above command has no hostname, indicating that we are activating for the local host.
Per-host home configurations
You may also have separate home configurations for each machine, such as legacyPackages.homeConfigurations."myuser@myhost"
. These can be activated using:
nix run .#activate $USER@$HOSTNAME
Remote Activation
nixos-unified
acts as a lightweight alternative to the various deployment tools such as deploy-rs
and colmena
. The .#activate
app takes the hostname as an argument. If you set the nixos-unified.sshTarget
option in your NixOS or nix-darwin configuration, it will run activation over the SSH connection.
Add the following to your configuration – nixosConfigurations.myhost
or darwinConfigurations.myhost
(depending on the platform):
{
nixos-unified.sshTarget = "myuser@myhost";
}
Then, you will be able to run the following to deploy to myhost
from any machine:
nix run .#activate myhost
Non-goals
Remote activation doesn’t seek to replace other deployment tools, and as such doesn’t provide features like rollbacks. It is meant for simple deployment use cases.
note
It is possible however that nixos-unified
can grow to support more sophisticated deployment capabilities
Flake Templates
We provide four templates, depending on your needs:
Available templates
You can easily initialize one of our templates using Omnix1:
If you do not use Omnix, you must use nix flake init
, and manually change the template values such as username and hostname.
NixOS only
NixOS configuration only, with home-manager
nix --accept-flake-config run github:juspay/omnix -- \
init -o ~/nix-config github:srid/nixos-unified#linux
macOS only
nix-darwin configuration only, with home-manager
nix --accept-flake-config run github:juspay/omnix -- \
init -o ~/nix-config github:srid/nixos-unified#macos
Home only
home-manager configuration only (useful if you use other Linux distros or do not have admin access to the machine)
nix --accept-flake-config run github:juspay/omnix -- \
init -o ~/nix-config github:srid/nixos-unified#home
After initializing the template
Run nix run .#activate
(nix run .#activate $USER
if you are using the last template, “Home only”) to activate the configuration.
- on macOS, if you get an error about
/etc/nix/nix.conf
, run:sudo mv /etc/nix/nix.conf /etc/nix/nix.conf.before-nix-darwin nix --extra-experimental-features "nix-command flakes" run .#activate
- on macOS, if you had used Determinate Systems nix-installer, you may want to uninstall that Nix, such that we use the one provided by nix-darwin,
sudo -i nix-env --uninstall nix
If you are on an Intel Mac, also change nixpkgs.hostPlatform
accordingly.
Module Arguments
Each of your NixOS, nix-darwin and home-manager modules implicitly receive a specialArgs
called flake
.
The components of this flake
attrset are:
Name | Description |
---|---|
inputs | The inputs of your flake; inputs.self referring to the flake itself |
config | The flake-parts perSystem config |
Here is an example of how these can be used:
{ flake, pkgs, lib, ... }:
let
inherit (flake) config inputs;
inherit (inputs) self;
in
{
imports = [
# Reference a flake input directly from a nix-darwin module
inputs.agenix.darwinModules.default
];
# Reference an arbitrary flake-parts config
home-manager.users.${config.me.username} = { };
}
While the above example uses a nix-darwin module, you can do the same on NixOS or home-manager modules.
Flake Outputs
Importing the nixos-unified
flake-parts module will autowire the following flake outputs in your flake:
Name | Description |
---|---|
nixos-unified.lib | Functions mkLinuxSystem , mkMacosSystem and mkHomeConfiguration |
packages.update | Flake app to update key flake inputs |
packages.activate | Flake app to build & activate the system (locally or remotely over SSH) or home configuration |
In addition, all of your NixOS/nix-darwin/home-manager modules implicitly receive the following specialArgs
:
flake@{self, inputs, config}
(config
is from flake-parts)rosettaPkgs
(if on darwin)
Autowiring
An optional autowiring module is provided that will scan the directory structure and wire up the appropriate flake outputs automatically without you having to do it manually.
A ready demonstration is available in nixos-unified-template as well as srid/nixos-config. In the latter, you will notice the following directory structure:
❮ lsd --tree --depth 1 configurations modules overlays packages
📁 configurations
├── 📁 darwin
├── 📁 home
└── 📁 nixos
📁 modules
├── 📁 darwin
├── 📁 flake-parts
├── 📁 home
└── 📁 nixos
📁 overlays
└── ❄️ default.nix
📁 packages
├── ❄️ git-squash.nix
├── ❄️ sshuttle-via.nix
└── 📁 twitter-convert
Each of these are wired to the corresponding flake output, as indicated in the below table:
Directory | Flake Output |
---|---|
configurations/nixos/foo.nix 1 | nixosConfigurations.foo |
configurations/darwin/foo.nix 1 | darwinConfigurations.foo |
configurations/home/foo.nix 1 | legacyPackages.${system}.homeConfigurations.foo 2 |
modules/nixos/foo.nix | nixosModules.foo |
modules/darwin/foo.nix | darwinModules.foo |
modules/flake-parts/foo.nix | flakeModules.foo |
overlays/foo.nix | overlays.foo |
flake-parts
Autowiring is also provided if you use just flake-parts, via the lib.mkFlake
function. In your top-level flake.nix, you only need to define your outputs
as follows:
{
inputs = ...;
outputs = inputs:
inputs.nixos-unified.lib.mkFlake
{ inherit inputs; root = ./.; };
}
This will,
- Auto-import flake-parts modules under either
./nix/modules/flake-parts
or./modules/flake-parts
(whichever exists) - Use a sensible default for
systems
which can be overriden. - Pass
root
as top-level module args, as a non-recursive way of referring to the path of the flake (without needinginputs.self
).
See srid/haskell-template’s flake.nix for a ready example. For another example, see this emanote PR.
This path could as well be configurations/nixos/foo/default.nix
. Likewise for other output types.
Why legacyPackages
? Because, creating a home-manager configuration requires pkgs
. See https://github.com/nix-community/home-manager/issues/3075
Examples
Release history
Unreleased
- autoWiring of flake outputs
0.2.0 (2024-10-03)
Initial release, branched from nixos-flake