--- title: Host Configuration Organization impact: HIGH impactDescription: Prevents configuration chaos across multiple hosts tags: hosts,modules,base-config,directory-structure --- ## Why This Matters Proper host configuration organization prevents duplication, makes maintenance easier, and ensures consistency across multiple machines. ## Directory Structure ``` nixos-config/ ├── flake.nix # Top-level flake ├── hosts/ │ ├── laptop/ │ │ ├── default.nix # Host-specific config │ │ ├── hardware-configuration.nix # Generated (don't edit) │ │ └── home.nix # Home Manager config │ ├── desktop/ │ │ ├── default.nix │ │ ├── hardware-configuration.nix │ │ └── home.nix │ └── base.nix # Shared by all hosts (optional) ├── modules/ # Reusable NixOS modules ├── overlays/ # Custom overlays ├── home-manager/ # Shared Home Manager configs │ ├── shell/ │ ├── applications/ │ └── common.nix └── secrets/ # Age-encrypted secrets ``` ## Host Configuration Pattern ### Incorrect: Everything in one file ```nix # hosts/laptop/default.nix { config, pkgs, ... }: { # Hardware config (copied from hardware-configuration.nix) boot.initrd.availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" ]; # ... 50 lines of hardware config ... # Network config networking.hostName = "laptop"; networking.networkmanager.enable = true; # User config users.users.john = { isNormalUser = true; extraGroups = [ "wheel" "networkmanager" ]; }; # Packages environment.systemPackages = with pkgs; [ vim git wget ]; # Services services.openssh.enable = true; # Home Manager imports = [ ./home.nix # Or inline the entire home config ]; } ``` **Problems:** - Hardware config duplicated (should import hardware-configuration.nix) - User config duplicated across hosts - No shared configuration - Hard to maintain ### Correct: Modular structure ```nix # hosts/laptop/default.nix { inputs, pkgs, pkgs-stable, system, ... }: { imports = [ ./hardware-configuration.nix # Import hardware config ../base.nix # Import shared config inputs.home-manager.nixosModules.home-manager { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; home-manager.users.john = import ./home.nix; home-manager.extraSpecialArgs = { inherit inputs pkgs-stable system; }; nixpkgs.overlays = [ inputs.some-overlay.overlays.default ]; } ]; # Host-specific config only networking.hostName = "laptop"; } ``` ## Shared Base Configuration ```nix # hosts/base.nix { config, pkgs, inputs, ... }: { # Network networking.networkmanager.enable = true; # Time and locale time.timeZone = "America/New_York"; i18n.defaultLocale = "en_US.UTF-8"; # Users (shared across all hosts) users.users.john = { isNormalUser = true; extraGroups = [ "wheel" "networkmanager" ]; }; # Common packages environment.systemPackages = with pkgs; [ vim git wget tmux ]; # Common services services.openssh.enable = true; # Nix settings nix.settings.experimental-features = [ "nix-command" "flakes" ]; } ``` ## Hardware Configuration ```nix # hosts/laptop/default.nix { ... }: { imports = [ ./hardware-configuration.nix # Generated by nixos-generate-config ../base.nix # ... ]; networking.hostName = "laptop"; } ``` **Important:** Don't edit `hardware-configuration.nix` manually. It's generated by `nixos-generate-config` and should be replaced when hardware changes. ## Home Manager Configuration ### Host-specific home.nix ```nix # hosts/laptop/home.nix { config, pkgs, inputs, ... }: { imports = [ ../../home-manager/shell ../../home-manager/applications ../../home-manager/common.nix ]; # Host-specific user config home.packages = with pkgs; [ laptop-specific-package ]; } ``` ### Shared Home Manager configs ```nix # home-manager/shell/default.nix { config, pkgs, ... }: { imports = [ ./fish ./neovim ./git ]; } # home-manager/common.nix { config, pkgs, ... }: { home.packages = with pkgs; [ jq ripgrep fzf ]; programs.git.enable = true; programs.git.userName = "John Doe"; programs.git.userEmail = "john@example.com"; } ``` ## Multiple Hosts Example ```nix # flake.nix outputs = { self, nixpkgs, home-manager, ... }@inputs: { nixosConfigurations.laptop = nixpkgs.lib.nixosSystem { specialArgs = { inherit inputs; }; modules = [ ./hosts/laptop ]; }; nixosConfigurations.desktop = nixpkgs.lib.nixosSystem { specialArgs = { inherit inputs; }; modules = [ ./hosts/desktop ]; }; nixosConfigurations.server = nixpkgs.lib.nixosSystem { specialArgs = { inherit inputs; }; modules = [ ./hosts/server ]; }; }; ``` Each host uses the same `base.nix` but can override settings: ```nix # hosts/server/default.nix { ... }: { imports = [ ../base.nix ./hardware-configuration.nix ]; # Override time zone for server time.timeZone = "UTC"; # Add server-specific packages environment.systemPackages = with pkgs; [ htop iotop ]; } ``` ## Module Pattern For reusable functionality, create modules: ```nix # modules/docker.nix { config, pkgs, ... }: { virtualisation.docker = { enable = true; enableOnBoot = true; }; users.users.john.extraGroups = [ "docker" ]; } ``` Use in host config: ```nix # hosts/laptop/default.nix { ... }: { imports = [ ../base.nix ../../modules/docker # Import module ./hardware-configuration.nix ]; } ``` ## Common Mistakes 1. **Editing hardware-configuration.nix**: Changes are lost when regenerating. Put host-specific config in `default.nix` instead. 2. **Duplicating config across hosts**: Use `base.nix` or modules for shared configuration. 3. **Not separating host-specific and user-specific**: Keep host config in `hosts/*/default.nix`, user config in `home.nix` or `home-manager/`. 4. **Mixing concerns**: Don't put shell config in system packages. Use Home Manager for user-level packages. ## Quick Checklist - [ ] Each host imports hardware-configuration.nix - [ ] Shared config in base.nix or modules - [ ] Home Manager configured in host file - [ ] Host-specific settings only in host's default.nix - [ ] User config in home.nix or home-manager/ - [ ] No duplication of hardware config - [ ] No duplication of user config