# External Configuration Modules Guide to using external modules for system and user configurations. ## Table of Contents - [Overview](#overview) - [System Modules](#system-modules) - [User Modules](#user-modules) - [Fetch Methods](#fetch-methods) - [Creating External Modules](#creating-external-modules) - [Best Practices](#best-practices) ## Overview External modules allow you to maintain configurations in separate Git repositories and reference them from Athenix. **Benefits:** - **Separation** - Keep complex configs in separate repositories - **Reproducibility** - Pin specific commits for deterministic builds - **Reusability** - Share configurations across multiple deployments - **Flexibility** - Mix external modules with local configuration - **Ownership** - Users maintain their own dotfiles ## System Modules External system modules provide host-specific NixOS configurations. ### Usage In `inventory.nix`, reference an external module using the `external` field: ```nix nix-lxc = { devices = { # Inline configuration (traditional method) "local-server" = { athenix.sw.type = "headless"; services.nginx.enable = true; }; # External module (lazy evaluation - fetched only when building this host) "remote-server".external = builtins.fetchGit { url = "https://git.factory.uga.edu/org/server-config"; rev = "abc123def456..."; # Must pin to specific commit }; # External module with additional local config "mixed-server" = { external = builtins.fetchGit { url = "https://git.factory.uga.edu/org/server-config"; rev = "abc123def456..."; }; # Additional local overrides athenix.users.admin.enable = true; services.openssh.permitRootLogin = "no"; }; }; }; ``` **Key Features:** - **Lazy Evaluation**: External modules are only fetched when building the specific host - **Efficient Rebuilds**: Other hosts can be rebuilt without fetching unrelated external modules - **Submodule Support**: Works with Git submodules without affecting other hosts ### Repository Structure ``` server-config/ ├── default.nix # Required: NixOS module ├── README.md # Recommended: Documentation └── optional/ ├── config/ # Optional: Configuration files └── scripts/ # Optional: Helper scripts ``` ### Module Content (default.nix) ```nix # The module receives inputs and standard NixOS module parameters { inputs, ... }: { config, lib, pkgs, ... }: { # Your NixOS configuration # Use any standard NixOS option or athenix.* options services.nginx = { enable = true; virtualHosts."example.com" = { root = "/var/www"; forceSSL = true; enableACME = true; }; }; # Use athenix options athenix.sw.type = "headless"; athenix.sw.extraPackages = with pkgs; [ git htop ]; # Standard NixOS configuration networking.firewall.allowedTCPPorts = [ 80 443 ]; services.openssh.enable = true; } ``` ### What System Modules Receive - **`inputs`** - All flake inputs (nixpkgs, home-manager, disko, etc.) - **`config`** - Current NixOS configuration (read/write) - **`lib`** - Nixpkgs library functions - **`pkgs`** - Package set ### Configuration Order When a host is built, modules load in this order: 1. Hardware type module (from `hw/nix-*.nix`) 2. Common system configuration (from `fleet/common.nix`) 3. Software type module (from `sw/{type}/`) 4. User NixOS modules (from `users.nix` - `nixos.nix` files) 5. Device-specific overrides (from `inventory.nix`) 6. External system module (if present) Each later module can override earlier ones using standard NixOS precedence rules. ## User Modules External user modules provide home-manager configurations (dotfiles, environment setup). ### Usage In `users.nix`, reference an external user module: ```nix athenix.users = { # External user module myuser.external = builtins.fetchGit { url = "https://git.factory.uga.edu/username/dotfiles"; rev = "abc123def456..."; # Pin to specific commit }; # Inline user definition otheruser = { description = "Other User"; extraGroups = [ "wheel" ]; shell = pkgs.zsh; hashedPassword = "$6$..."; }; }; ``` Then enable on hosts in `inventory.nix`: ```nix nix-laptop = { devices = 5; overrides.athenix.users.myuser.enable = true; }; ``` ### Repository Structure ``` my-dotfiles/ ├── user.nix # Required: User options + home-manager config ├── nixos.nix # Optional: System-level configuration ├── README.md # Recommended: Documentation └── config/ # Optional: Your actual dotfiles ├── zshrc ├── vimrc ├── nvim/ └── ... ``` ### user.nix (Required) Provides both user account settings AND home-manager configuration: ```nix # Receives { inputs } and standard home-manager module parameters { inputs, ... }: { config, lib, pkgs, osConfig ? null, ... }: { # ========== User Account Configuration ========== # These options define the user account itself athenix.users.myusername = { description = "My Full Name"; extraGroups = [ "wheel" "docker" ]; shell = pkgs.zsh; hashedPassword = "!"; # SSH keys only opensshKeys = [ "ssh-ed25519 AAAA... user@laptop" ]; useZshTheme = true; useNvimPlugins = true; }; # ========== Home Manager Configuration ========== # User environment, packages, and dotfiles # Packages home.packages = with pkgs; [ vim git ripgrep fzf ] ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox; # Programs programs.git = { enable = true; userName = "My Name"; userEmail = "me@example.com"; extraConfig = { init.defaultBranch = "main"; core.editor = "vim"; }; }; programs.zsh = { enable = true; initExtra = '' # Your Zsh configuration export EDITOR=vim ''; }; # Manage dotfiles home.file.".zshrc".source = ./config/zshrc; home.file.".vimrc".source = ./config/vimrc; home.file.".config/nvim".source = ./config/nvim; # Services services.gpg-agent.enable = true; } ``` ### nixos.nix (Optional) System-level configuration for this user (rarely needed): ```nix { inputs, ... }: { config, lib, pkgs, ... }: { # System-level configuration # Only needed if the user requires specific system-wide settings users.users.myusername.extraGroups = [ "docker" ]; environment.systemPackages = [ pkgs.docker ]; # Security settings security.sudo.extraRules = [{ users = [ "myusername" ]; commands = [{ command = "/usr/bin/something"; options = [ "NOPASSWD" ]; }]; }]; } ``` ### What User Modules Receive **In user.nix:** - **`inputs`** - All flake inputs (nixpkgs, home-manager, etc.) - **`config`** - Home-manager configuration (read/write) - **`lib`** - Nixpkgs library functions - **`pkgs`** - Package set - **`osConfig`** - OS configuration (read-only) - useful for conditional setup **In nixos.nix:** - **`inputs`** - Flake inputs - **`config`** - NixOS configuration (read/write) - **`lib`** - Nixpkgs library functions - **`pkgs`** - Package set ### Conditional Setup Example Use `osConfig` to conditionally set up dotfiles based on the system type: ```nix # In user.nix { inputs, ... }: { config, lib, pkgs, osConfig ? null, ... }: { athenix.users.myuser = { /* ... */ }; # Install Firefox only on desktop systems home.packages = with pkgs; [ ripgrep ] ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox; # Different shell config per system programs.zsh.initExtra = '' ${lib.optionalString (osConfig.athenix.sw.type or null == "headless") " # Headless-only settings "} ''; } ``` ## Fetch Methods ### builtins.fetchGit (Recommended) Pin to a specific Git revision: ```nix builtins.fetchGit { url = "https://git.factory.uga.edu/username/dotfiles"; rev = "abc123def456..."; # Required: specific commit hash } ``` **Advantages:** - Reproducible (pinned to exact commit) - Works with any Git repository - Supports SSH or HTTPS URLs **Important:** Always specify `rev` (commit hash) for reproducibility. Don't use branches which can change. ### builtins.fetchTarball Download specific release archives: ```nix builtins.fetchTarball { url = "https://github.com/user/repo/archive/v1.0.0.tar.gz"; sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; } ``` **Get the hash:** ```bash nix-prefetch-url --unpack https://github.com/user/repo/archive/v1.0.0.tar.gz ``` ### Local Path (For Testing) Use local directories during development: ```nix # users.nix athenix.users.myuser.external = /home/user/my-dotfiles; # inventory.nix nix-laptop = { devices = { "dev".athenix.users.myuser.enable = true; }; }; ``` **Note:** Only works if the path exists on the machine running `nix flake check` or `nix build`. ## Creating External Modules ### System Module Template Create a new system module repository from the template: ```bash nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#system ``` This creates: ``` my-system-config/ ├── flake.nix # Optional: for testing standalone ├── default.nix # Your NixOS module └── README.md # Documentation ``` ### User Module Template Create a new user module repository: ```bash nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#user ``` This creates: ``` my-dotfiles/ ├── flake.nix # Optional: for testing standalone ├── user.nix # User options + home-manager config ├── nixos.nix # Optional: system-level config └── README.md # Documentation ``` ### Testing External Modules Test your external module locally before pushing: ```bash # In your module repository cd /path/to/my-module # Test the Nix syntax nix flake check ``` ## Best Practices ### 1. Always Pin to Specific Commits ❌ Wrong - using branch names: ```nix builtins.fetchGit { url = "https://git.factory.uga.edu/username/dotfiles"; # No rev specified or using "main" } ``` ✅ Correct - using commit hash: ```nix builtins.fetchGit { url = "https://git.factory.uga.edu/username/dotfiles"; rev = "abc123def456789..."; } ``` ### 2. Keep External Modules Focused Each external module should have a clear purpose: - User dotfiles (one repo per user) - System service configuration (one repo per service/cluster) - Hardware-specific config (one repo per hardware setup) ### 3. Document Your Modules Include a README with: - What the module configures - Required dependencies - Usage examples - Configuration options ### 4. Use Semantic Versioning Tag releases in Git: ```bash git tag v1.0.0 git push origin v1.0.0 ``` Reference specific versions: ```nix builtins.fetchGit { url = "https://git.factory.uga.edu/org/server-config"; rev = "v1.0.0"; # Can use tags too } ``` ### 5. Test Before Updating Pins When updating commit hashes: ```bash # Test new revision locally nix flake update # Validate all configurations nix flake check --show-trace # Only commit after validation git add . && git commit -m "Update module versions" ``` ## See Also - [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management - [INVENTORY.md](INVENTORY.md) - Host configuration - [NAMESPACE.md](NAMESPACE.md) - Configuration options - [README.md](../README.md) - Main documentation - [templates/user/](../templates/user/) - User module template - [templates/system/](../templates/system/) - System module template