An extensible framework to build rock-solid, up-to-date NixOS hosts
Powered by Modulon π€ β a plug-and-play module management framework for NixOS flakes
A modular, flexible NixOS flake for multi-host, multi-channel setups β enabling varied system combinations while staying compatible with thirdβparty flakes:
- Stable release with stable packages (a reliable workhorse)
- Stable release with unstable packages (best of both worlds)
- Development snapshot with unstable packages (bleeding edge)
- Development snapshot with selected stable packages (for compatibility needs)
While fully usable (I use it daily), this flake is still under heavy development. Bugs may show up for lunch. Use at your own risk! π₯
- Table of Contents
- Overview
- Key Features
- How It Works
- Multi-Channel Support
- Getting Started
- Customization Examples
- Visual Workflow
- Contributing
- License
This NixOS flake configuration is built to manage multiple hosts (e.g., laptops, desktops, servers) with a modular approach, separating core system setup, host-specific settings, and user configurations via Home Manager. It introduces a robust multi-channel support system, allowing hosts to independently use either the stable or unstable Nixpkgs channel without requiring separate flakes or manual input adjustments. This setup is ideal for users who need to balance reliability (stable) with cutting-edge features (unstable) across different machines.
- Multi-Host Management: Configure distinct hosts (e.g.,
desktop,tuxedo,chuwi) with tailored settings from a single flake. - Multi-Channel Support: Choose between stable (
nixos-24.11) and unstable (nixos-unstable) channels per host, with automatic alignment of third-party flake inputs. - Modular Design: Separate NixOS system configurations and Home Manager user settings into reusable, maintainable modules.
- Dynamic Input Normalization: Automatically filter and normalize third-party flake inputs based on the selected channel, ensuring compatibility and consistent referencing.
- Extensibility: Easily add new hosts, modules, or third-party dependencies with minimal configuration overhead.
The flake is organized into several key directories and files:
flake.nix: The core entry point defining inputs (Nixpkgs, third-party flakes), outputs (NixOS configurations), and the logic for channel selection and input normalization.configs/nixos/: Contains host-specific configurations (configuration.nix,profile.nix) and shared modules for system settings (e.g., networking, security).configs/home-manager/: Manages user-specific settings and packages via Home Manager, with a dynamic module loader inhome.nix.lib/: Custom libraries or utilities (e.g., ANSI color formatting) used across configurations.secrets/: Handles encrypted secrets for secure configuration (e.g., usingagenix).
Below is an overview of the directory structure for this NixOS flake configuration, providing a clear view of how files and configurations are organized:
.
βββ assets/ # Media assets for the repository documentation
βββ configs/ # All system configurations
β βββ home-manager/ # Home Manager configurations
β β βββ modules/ # Home Manager modules
β β βββ users/ # User-specific configurations
β βββ nixos/ # NixOS configurations
β βββ hosts/ # Host-specific configurations
β βββ modules/ # NixOS modules
β βββ overlays/ # NixOS overlays
βββ flake.lock
βββ flake.nix
βββ lib/ # Shared libraries and helper tools
βββ secrets # Where the flake looks for the secrets
βββ README.md
βββ tests/ # Tests for modules
βββ home-manager # Home Manager tests
βββ nixos/ # NixOS tests
This structure ensures a modular and organized approach, separating system-wide NixOS settings, user-specific Home Manager configurations, and supporting utilities or tests into distinct directories for clarity and maintainability.
- Base Configuration: Default system settings are defined in shared modules under
configs/nixos/modules/, applied to all hosts. - Host-Specific Settings: Each host has a dedicated directory (e.g.,
configs/nixos/hosts/desktop/) withconfiguration.nixfor core setup andprofile.nixfor overrides or custom options. - Dynamic Modules: The
modulonframework enables dynamic loading of NixOS modules, enhancing extensibility.
- Entry Point:
configs/home-manager/home.nixserves as the starting point for user configurations, integrating with NixOS via a module. - User Profiles: User-specific settings (e.g., shell configurations, dotfiles) are modularized under
configs/home-manager/users/andmodules/, loaded dynamically.
This flake implements a sophisticated mechanism to support both stable and unstable Nixpkgs channels within a single configuration, addressing the challenge of aligning third-party flake inputs with the appropriate channel.
- Stable and Unstable Inputs: For each third-party flake dependency, both stable and unstable variants are defined in the
inputsattribute set. Stable inputs typically follow thenixpkgschannel (e.g.,nixos-24.11), while unstable variants follownixpkgs-unstableand often point to the latest or master branch of the respective repository.- Example:
home-managerfollowsnixpkgswith a specific release tag, whilehome-manager-unstablefollowsnixpkgs-unstableand tracks the master branch.
- Example:
- Naming Convention: Unstable variants are suffixed with
-unstable(e.g.,nixvim-unstable), while stable variants either have no suffix or use-stableif explicit distinction is needed. Channel-agnostic inputs (those without a specific channel dependency) may not have suffixes.
- Channel Map Definition: Within the
mkHostConfigfunction, achannelMapattribute defines two channels:stableandunstable. Each channel specifies:p: The Nixpkgs source (nixpkgsfor stable,nixpkgs-unstablefor unstable)._inputs: A normalized set of third-party inputs tailored to the channel, constructed viabuildChannelInputsSet.
- Input Filtering and Normalization:
- The
buildChannelInputsSetfunction filters inputs based on the channel:- For
unstable, it selects inputs with the-unstablesuffix. - For
stable, it selects inputs without the-unstablesuffix (including those with-stableor no suffix).
- For
- Channel-agnostic inputs (those without suffixes) are included in both channels.
- Suffixes are stripped to create normalized names (e.g.,
nixvim-unstablebecomesnixvimfor the unstable channel,nixvimremainsnixvimfor stable), ensuring consistent referencing in modules regardless of channel.
- The
- Selection: The
releaseChannelis determined by the hostβschannelattribute (stableorunstable), selecting the appropriate Nixpkgs source and input set.
- Special Arguments: The normalized input set is passed to modules via
specialArgsas_inputs = releaseChannel._inputs;. This allows modules to reference third-party flakes consistently as_inputs.<normalizedName>(e.g.,_inputs.home-manageror_inputs.nix-snapd), regardless of whether the underlying input is from the stable or unstable variant. - Direct Access in Core Logic: Within
mkHostConfig, inputs are accessible asreleaseChannel._inputs.<normalizedName>for module imports or configurations.
- Channel Selection: Each host in the
hostsattribute set specifies achannelattribute (stableorunstable), determining which Nixpkgs source and input set are used viachannelMap. - Unstable Overlays: For hosts using the
unstablechannel, an overlay is added to provide access to stable packages viastablePkgs, allowing selective use of stable packages if needed.
- Defining Host Channel: Set the
channelattribute in a hostβs configuration to either"stable"or"unstable"to select the desired Nixpkgs channel and corresponding third-party inputs.- Example: In
hosts.tuxedo, settingchannel = "unstable";usesnixpkgs-unstableand unstable variants of inputs likehome-manager-unstable.
- Example: In
- Referencing Inputs in Modules: Use
_inputs.<normalizedName>to access third-party flakes in custom modules, ensuring channel compatibility without hardcoding specific variants.- Example:
_inputs.nixvim.nixosModules.nixvimworks for both stable and unstable channels due to normalized naming.
- Example:
- Single Flake, Multiple Channels: Manage hosts on different channels within one flake, avoiding the need for separate flakes for stable and unstable configurations.
- Third-Party Compatibility: Dynamically align third-party flakes with the hostβs channel, preventing version mismatches without manual intervention.
- Simplified Maintenance: Normalized input names reduce code duplication and simplify module imports, as the same reference works across channels.
- Input Variants Required: Each third-party flake must have stable and unstable variants defined in
inputs, which may require manual updates if new dependencies are added. - Upstream Channel Support: Some third-party flakes may not fully support both channels or may require additional configuration to align with
nixpkgs-unstable. Ensure upstream compatibility when using the unstable channel. - Testing: Thoroughly test host configurations after switching channels, as differences in package versions or flake behaviors may introduce issues.
- NixOS with Flake Support: Ensure you have NixOS installed with flake support enabled (
nix.flakesin your configuration or vianix --experimental-features 'nix-command flakes'). - Basic Nix Knowledge: Familiarity with Nix expressions, modules, and flakes will help in navigating and customizing this configuration.
- Clone the Repository: If this flake is hosted on GitHub or another repository, clone it to your system:
git clone https://github.com/cig0/nixos-config.git cd nixos-config - Review Hosts: Check the
hostsattribute inflake.nixto see predefined hosts (chuwi,desktop,tuxedo) and their configurations. - Build a Host Configuration: Use
nixos-rebuildto build and apply a configuration for a specific host:Replacesudo nixos-rebuild switch --flake .#desktopdesktopwith the desired host name from thehostsset. - Customize: Follow the customization examples below to add new hosts or modify settings.
To add a new host named laptop using the stable channel:
- Create Host Directory: Add a directory for the new host under
configs/nixos/hosts/laptop/. - Define Configuration Files:
- Create
configs/nixos/hosts/laptop/configuration.nixwith core system settings (e.g., bootloader, networking). - Create
configs/nixos/hosts/laptop/profile.nixwith host-specific options or overrides.
- Create
- Update Hosts in
flake.nix: Add the host to thehostsattribute set:laptop = { description = "New Laptop: AMD CPU & GPU + GNOME"; channel = "stable"; system = "x86_64-linux"; extraModules = [ ]; };
- Build and Apply: Build the configuration for the new host:
sudo nixos-rebuild switch --flake .#laptop
To switch an existing host (e.g., tuxedo) to the unstable channel:
- Update Channel Attribute: In
flake.nix, modify thechannelfor the host:tuxedo = { description = "Laptop: Intel CPU & GPU + KDE"; channel = "unstable"; # Changed from "stable" system = "x86_64-linux"; extraModules = [ inputs.nixos-hardware.nixosModules.tuxedo-infinitybook-pro14-gen7 ]; };
- Rebuild Configuration: Rebuild the host to apply the unstable channel and associated inputs:
The system will now use
sudo nixos-rebuild switch --flake .#tuxedonixpkgs-unstableand unstable variants of third-party flakes.
To add a new module for a custom application or setting:
- Create Module File: Add a new module under
configs/nixos/modules/applications/or another relevant directory (e.g.,my-app.nix). - Define Configuration: Specify the desired settings in the module, referencing
_inputsif using third-party flakes:{ _inputs, ... }: { imports = [ _inputs.my-app.nixosModules.my-app ]; # Additional configurations }
- Ensure Dynamic Loading: The
modulonframework automatically loads modules from specified directories, so no further changes are needed if placed inconfigs/nixos/modules/.
Below is a simplified flowchart illustrating how channel selection and input normalization work in this flake, making the process easier to grasp visually:
graph TD
A[Host Definition] -->|channel = stable or unstable| B[Select releaseChannel from channelMap]
B --> C{channelMap}
C -->|stable| D[p: nixpkgs, _inputs: buildChannelInputsSet 'stable']
C -->|unstable| E[p: nixpkgs-unstable, _inputs: buildChannelInputsSet 'unstable']
D --> F[Filter inputs: no -unstable suffix + agnostic inputs]
E --> G[Filter inputs: -unstable suffix + agnostic inputs]
F --> H[Normalize names: strip -stable if present]
G --> I[Normalize names: strip -unstable]
H --> J[Pass _inputs to specialArgs]
I --> J
J --> K[Modules access via _inputs.<name>]
K --> L[Build NixOS Configuration]
This diagram shows the flow from host channel selection to input filtering, normalization, and usage in modules, ensuring channel-specific compatibility.
Contributions to improve this flake configuration are welcome! Whether it's bug fixes, new features, or documentation enhancements, feel free to submit pull requests or open issues on the GitHub repository (once published). Please ensure changes are tested across both stable and unstable channels if relevant.
Unless otherwise stated, everything in this repo is covered by the following copyright notice:
A NixOS multi-host, multi-channel setups flake.
Copyright (C) 2024-2025 MartΓn Cigorraga <[email protected]>
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License v3 or later, as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
