Feature Flags¶
The problem¶
Enabling a capability often means configuring several services together. Doing that inline in each host file produces copy-paste, drift, and no way to validate that prerequisites are met.
The solution¶
Capabilities are exposed as typed feature flags under features.*. A host
turns a dial; the module behind it wires up the actual services. The shared
option declarations live in
modules/common/features.nix,
which groups them as development, virtualization, cloud, security,
networking, ai, programs, media and quickshell. Individual services
declare their own flag in their own module instead.
# In a host configuration.nix — declarative intent, not implementation
features = {
development.enable = true;
virtualization = {
enable = true;
docker = true;
};
ai.enable = true;
media.enable = true;
syncthing.enable = true;
};
How a feature module is shaped¶
Every module follows the same contract: declare options, then apply config only when enabled.
{ config, lib, pkgs, ... }:
let
cfg = config.features.myservice;
in
{
options.features.myservice = {
enable = lib.mkEnableOption "MyService";
port = lib.mkOption {
type = lib.types.port;
default = 8080;
description = "Port MyService listens on.";
};
};
config = lib.mkIf cfg.enable {
services.myservice = {
enable = true;
port = cfg.port;
};
};
}
Never configure services directly in a host
services.foo = { … } in hosts/*/configuration.nix defeats reuse and
testing. Wrap it in a module under modules/ and expose a feature flag.
This is a hard rule — see Anti-Patterns.
Host types¶
There is no central registry of feature dependencies or conflicts, and no
cross-feature validation pass — a module that needs a prerequisite asserts it
itself, with assertions.
What does exist is
lib/hostTypes.nix,
which pre-composes flags for a role. Two types are defined:
| Type | Sets |
|---|---|
workstation |
development, desktop, virtualization |
laptop |
development, desktop, virtualization (no Docker), powerManagement |
Both are set with lib.mkDefault, so a host that assigns the flag directly
always wins:
# hosts/razer/configuration.nix — overrides the laptop default
features.virtualization.docker = true;
Each host imports one type plus its own hardware configuration.
Browsing what exists¶
The full set of feature-flagged modules — every option, with its description and the source — is in the generated Modules reference.