Skip to content

Specialisations

NullString1 edited this page Jan 13, 2026 · 1 revision

Specialisations

Boot-time system configurations for different use cases in NullOS.

Overview

Specialisations allow you to have multiple NixOS configurations in one system, selectable at boot time via GRUB menu. Common uses:

  • GPU switching (integrated vs. dedicated)
  • Different kernel versions
  • Alternative desktop environments
  • Development vs. production modes

Current Specialisations

NVIDIA GPU Switching

NullOS implements GPU switching for hybrid graphics systems:

Base Configuration:

specialisation = lib.mkIf vars.useNvidiaPrime {
  nvidia.configuration = {
    system.nixos.tags = [ "nvidia" ];
    
    hardware.nvidia = {
      prime.offload.enable = lib.mkForce false;
      prime.sync.enable = lib.mkForce true;
    };
  };
};

This creates two boot options:

  1. Default - Intel integrated GPU (power saving)
  2. nvidia - NVIDIA GPU always active (performance)

How It Works

Boot Menu

When booting, GRUB shows:

NixOS - Default
NixOS - nvidia
NixOS - configuration.nix (previous generation)

Select the specialisation you want to use.

System Tags

Tags appear in system information:

cat /run/current-system/nixos-version
# Shows: 24.11.20250113.dirty (nvidia)

NVIDIA Specialisation Details

Offload Mode (Default)

Power saving mode:

hardware.nvidia.prime = {
  offload = {
    enable = true;
    enableOffloadCmd = true;
  };
  
  intelBusId = "PCI:0:2:0";
  nvidiaBusId = "PCI:1:0:0";
};

Features:

  • Intel GPU used by default
  • NVIDIA GPU powered off when not needed
  • Longer battery life
  • Explicit GPU selection with nvidia-offload command

Usage:

# Run specific app on NVIDIA
nvidia-offload glxinfo | grep "OpenGL renderer"
nvidia-offload minecraft

Sync Mode (Specialisation)

Performance mode:

hardware.nvidia.prime = {
  sync.enable = true;
  
  intelBusId = "PCI:0:2:0";
  nvidiaBusId = "PCI:1:0:0";
};

Features:

  • NVIDIA GPU always active
  • Maximum performance
  • Higher power consumption
  • All applications use NVIDIA by default

When to use:

  • Gaming sessions
  • 3D rendering
  • Video editing
  • Machine learning
  • External displays (if wired directly to dGPU)

Creating Specialisations

Basic Syntax

In any NixOS configuration file:

{ config, pkgs, lib, ... }:

{
  specialisation.name = {
    configuration = {
      # Configuration overrides
    };
  };
}

Example: Different Kernel

specialisation.latest-kernel = {
  configuration = {
    system.nixos.tags = [ "latest" ];
    
    boot.kernelPackages = pkgs.linuxPackages_latest;
  };
};

Creates boot option with latest kernel instead of LTS.

Example: Gaming Mode

specialisation.gaming = {
  configuration = {
    system.nixos.tags = [ "gaming" ];
    
    # Performance optimizations
    powerManagement.cpuFreqGovernor = "performance";
    
    # Additional packages
    environment.systemPackages = with pkgs; [
      gamemode
      mangohud
    ];
    
    # Services
    programs.gamemode.enable = true;
    
    # Kernel parameters
    boot.kernelParams = [
      "mitigations=off"  # Security vs performance trade-off
    ];
  };
};

Example: Different Desktop

specialisation.kde = {
  configuration = {
    system.nixos.tags = [ "kde" ];
    
    # Disable Hyprland
    programs.hyprland.enable = lib.mkForce false;
    
    # Enable KDE
    services.xserver.enable = true;
    services.desktopManager.plasma6.enable = true;
    
    services.displayManager.sddm = {
      enable = true;
      wayland.enable = true;
    };
  };
};

Multiple Specialisations

You can have multiple specialisations:

specialisation = {
  nvidia = {
    configuration = {
      system.nixos.tags = [ "nvidia" ];
      # NVIDIA config
    };
  };
  
  gaming = {
    configuration = {
      system.nixos.tags = [ "gaming" ];
      # Gaming config
    };
  };
  
  nvidia-gaming = {
    configuration = {
      system.nixos.tags = [ "nvidia" "gaming" ];
      # Combined config
    };
  };
};

This creates boot menu:

  • NixOS - Default
  • NixOS - nvidia
  • NixOS - gaming
  • NixOS - nvidia-gaming

Configuration Inheritance

Specialisations inherit base configuration, then override:

# Base configuration
services.printing.enable = true;
programs.hyprland.enable = true;

specialisation.minimal = {
  configuration = {
    # Inherited: printing still enabled
    # Override: disable Hyprland
    programs.hyprland.enable = lib.mkForce false;
  };
};

Use lib.mkForce to override existing values.


Checking Current Specialisation

Via System Tags

cat /run/current-system/specialisation
# Shows: nvidia (if in specialisation)
# Empty if default

Via Environment

Specialisations can set environment variables:

specialisation.nvidia.configuration = {
  environment.variables.SPECIALISATION = "nvidia";
};

Then check:

echo $SPECIALISATION

Per-Machine Specialisations

Only enable on specific machines:

specialisation = lib.mkIf (vars.hostname == "nslapt") {
  nvidia = {
    configuration = {
      # Laptop-specific NVIDIA config
    };
  };
};

Or:

specialisation = lib.mkIf vars.useNvidiaPrime {
  nvidia = {
    configuration = {
      # Only on machines with NVIDIA
    };
  };
};

Specialisation Use Cases

Development vs Production

specialisation.development = {
  configuration = {
    system.nixos.tags = [ "dev" ];
    
    # Development tools
    environment.systemPackages = with pkgs; [
      gdb
      valgrind
      strace
    ];
    
    # Allow insecure packages for testing
    nixpkgs.config.allowInsecure = true;
    
    # Debug kernel
    boot.kernelPackages = pkgs.linuxPackages_latest;
    boot.kernelParams = [ "debug" ];
  };
};

Testing Configuration

specialisation.testing = {
  configuration = {
    system.nixos.tags = [ "testing" ];
    
    # Test new software
    environment.systemPackages = with pkgs; [
      experimental-package
    ];
    
    # Override unstable packages
    nixpkgs.config.packageOverrides = pkgs: {
      firefox = pkgs.firefox-beta-bin;
    };
  };
};

Rescue Mode

specialisation.rescue = {
  configuration = {
    system.nixos.tags = [ "rescue" ];
    
    # Minimal services
    systemd.services = lib.mkForce {};
    
    # Shell access
    systemd.services.rescue = {
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        Type = "simple";
        ExecStart = "${pkgs.bash}/bin/bash";
      };
    };
  };
};

Building Specialisations

All Configurations

Normal rebuild builds all specialisations:

sudo nixos-rebuild switch --flake .#hostname

Specific Specialisation Only

# Build but don't activate
sudo nixos-rebuild build --flake .#hostname

# Manually activate specialisation
/nix/var/nix/profiles/system/specialisation/nvidia/bin/switch-to-configuration test

GRUB Configuration

Default Boot

Set default specialisation:

boot.loader.grub = {
  default = "saved";  # Remember last choice
  # or
  default = 0;  # First entry (default)
  default = 1;  # Second entry (first specialisation)
};

Timeout

boot.loader.timeout = 5;  # 5 seconds to choose

Hidden Menu

boot.loader.grub.configurationLimit = 10;  # Keep last 10 generations

Managing Specialisation Disk Space

Each specialisation is a separate configuration:

# Check sizes
du -sh /nix/var/nix/profiles/system*

# Clean old generations
sudo nix-collect-garbage --delete-older-than 30d

# Keep only specific generations
sudo nix-env --profile /nix/var/nix/profiles/system --delete-generations +5

Troubleshooting

Specialisation Won't Boot

Boot into default configuration:

  1. Select default in GRUB
  2. Fix specialisation configuration
  3. Rebuild

Can't Switch Back

# Switch back to default
sudo /nix/var/nix/profiles/system/bin/switch-to-configuration switch

Configuration Conflicts

# In specialisation, use lib.mkForce
specialisation.nvidia.configuration = {
  # This will override base config
  hardware.nvidia.prime.offload.enable = lib.mkForce false;
};

Best Practices

  1. Use Tags - Makes specialisation visible in system info
  2. Test First - Test specialisation before committing
  3. Document Purpose - Comment why specialisation exists
  4. Limit Number - Too many = confusing boot menu
  5. Clean Old Generations - Specialisations multiply disk usage

Advanced: Nested Specialisations

Not recommended, but possible:

specialisation.a = {
  configuration = {
    specialisation.b = {
      configuration = {
        # Nested config
      };
    };
  };
};

This creates:

  • Default
  • a
  • a → b

Checking Available Specialisations

# List specialisations
ls /nix/var/nix/profiles/system/specialisation/

# View configuration
cat /nix/var/nix/profiles/system/specialisation/nvidia/nixos-version

Removing Specialisations

Comment out or remove from configuration:

# specialisation.nvidia = {
#   configuration = {
#     # ...
#   };
# };

Then rebuild:

sudo nixos-rebuild switch --flake .#hostname

Old specialisations remain in previous generations until garbage collected.


Example: Complete Setup

{ config, pkgs, lib, vars, ... }:

{
  # Base configuration
  programs.hyprland.enable = true;
  
  # GPU specialisation
  specialisation = lib.mkIf vars.useNvidiaPrime {
    nvidia-performance = {
      inheritParentConfig = true;
      
      configuration = {
        system.nixos.tags = [ "nvidia" "performance" ];
        
        # Force NVIDIA active
        hardware.nvidia.prime = {
          offload.enable = lib.mkForce false;
          sync.enable = lib.mkForce true;
        };
        
        # Performance tuning
        powerManagement.cpuFreqGovernor = lib.mkForce "performance";
        
        # Gaming packages
        environment.systemPackages = with pkgs; [
          gamemode
          mangohud
        ];
        
        programs.gamemode.enable = true;
        
        # Set environment variable
        environment.variables.GPU_MODE = "nvidia";
      };
    };
  };
}

Next Steps

Clone this wiki locally