Skip to content

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 feature system (lib/features.nix) additionally validates dependencies and conflicts at evaluation time.

# 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.

Dependency and conflict validation

lib/features.nix carries a small registry describing how features relate. When a host enables a feature, the system asserts its dependencies are present and that no conflicting feature is active:

development = {
  dependencies = [ "networking" ];
  conflicts = [ ];
  description = "Development environment setup";
};
gaming = {
  dependencies = [ "graphics" ];
  conflicts = [ "server-minimal" ];
};

If you enable a feature without its dependency, the build fails fast with a clear message — far better than a service silently misbehaving at runtime.

Feature profiles

Common bundles are pre-composed so a host can opt into a whole role at once:

Profile Enables
workstation development, desktop, virtualization, security
gaming desktop, gaming, security
server virtualization, security, networking
minimal security

Explicit per-feature flags always take precedence over profile defaults.

Browsing what exists

The full set of feature-flagged modules — every option, with its description and the source — is in the generated Modules reference.