Creating a new module
1. Create the module file
Create a new file in the appropriate directory:
modules/nixos/ — System-level configuration
modules/home/ — User-level configuration
modules/generic/ — Shared between both
2. Write the module
Follow the standard module structure:
{ config, lib, pkgs, ... }:
let
cfg = config.marchyo;
in
{
config = lib.mkIf cfg.myFeature.enable {
# Your configuration here
environment.systemPackages = with pkgs; [
some-package
];
};
}
3. Register the import
Add the file to the corresponding default.nix:
# modules/nixos/default.nix
{
imports = [
./my-feature.nix
# ... other imports
];
}
4. Define options
Add any new options to modules/nixos/options.nix under the marchyo.* namespace:
myFeature = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable my feature";
};
};
5. Add a test
Add an evaluation test in tests/module-tests.nix:
eval-my-feature = testNixOS "my-feature" (withTestUser {
marchyo.myFeature.enable = true;
});
Home Manager modules
Home Manager modules that need NixOS config use osConfig:
{ config, lib, osConfig ? {}, ... }:
let
cfg = osConfig.marchyo or {};
in
{
config = lib.mkIf (cfg ? myFeature && cfg.myFeature.enable) {
# User-level configuration
};
}
Best practices
- Use
lib.mkIf for feature-gated configuration blocks
- Use
lib.mkDefault for values consumers should be able to override
- Use
lib.mkMerge when a module has multiple conditional branches
- Define all options in
options.nix — never define marchyo.* options in individual modules
- Add a test for every new feature flag
Last modified on April 8, 2026