Files
athenix/docs/EXTERNAL_MODULES.md
UGA Innovation Factory 97646f3229 docs: update documentation for refactored structure
- Update NAMESPACE.md: filesystem.device and swapSize defaults now null
- Update README.md: add 'Using Athenix as a Library' section with lib.mkFleet
- Update copilot-instructions.md: reflect new directory structure and defaults
- Update EXTERNAL_MODULES.md: correct paths from variants/ and glue/ to hw/ and fleet/
- Update PROXMOX_LXC.md: update hardware type path reference
2026-01-07 18:12:39 -05:00

484 lines
11 KiB
Markdown

# 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