391 lines
12 KiB
Markdown
391 lines
12 KiB
Markdown
|
|
# Architecture & Design Principles
|
||
|
|
|
||
|
|
An overview of how your portable NixOS configuration is structured and why.
|
||
|
|
|
||
|
|
## Core Design Goals
|
||
|
|
|
||
|
|
1. **Portability**: Deploy to new machines, existing NixOS, or non-NixOS with minimal effort
|
||
|
|
2. **Modularity**: Enable/disable features independently without tight coupling
|
||
|
|
3. **Reproducibility**: Flakes + lock files ensure identical environments across machines
|
||
|
|
4. **Maintainability**: Clear separation of concerns (system vs home, shared vs per-machine)
|
||
|
|
5. **Secrets Management**: Encrypted, portable secrets with sops-nix
|
||
|
|
6. **Scalability**: Easy to add new machines and modules
|
||
|
|
|
||
|
|
## Architecture Overview
|
||
|
|
|
||
|
|
```
|
||
|
|
nix-los/
|
||
|
|
├── flake.nix # Single source of truth for all configurations
|
||
|
|
│ # Defines: inputs, outputs, configurations
|
||
|
|
│
|
||
|
|
├── hosts/ # Per-machine specific overrides
|
||
|
|
│ ├── laptop/ # Machine-specific hardware, features
|
||
|
|
│ └── server/ # (template for new machines)
|
||
|
|
│
|
||
|
|
├── nixos/ # System-level modules (NixOS only)
|
||
|
|
│ ├── default.nix # Imports all modules
|
||
|
|
│ └── modules/ # Toggleable features
|
||
|
|
│ ├── system.nix # Users, sudo, locale
|
||
|
|
│ ├── development.nix # Languages, tools
|
||
|
|
│ ├── shell.nix # Shell config
|
||
|
|
│ └── secrets-example.nix (inactive)
|
||
|
|
│
|
||
|
|
├── home/ # User-level configuration (portable)
|
||
|
|
│ ├── default.nix # Imports all modules, can run standalone
|
||
|
|
│ └── modules/ # User-level features
|
||
|
|
│ ├── shell.nix # Zsh, direnv, starship
|
||
|
|
│ ├── editor.nix # Neovim, VSCode
|
||
|
|
│ ├── git.nix # Git configuration
|
||
|
|
│ └── dev-tools.nix # Tmux, utilities
|
||
|
|
│
|
||
|
|
├── secrets/ # Encrypted credentials
|
||
|
|
│ ├── .sops.yaml # Encryption keys (DO NOT COMMIT UNENCRYPTED)
|
||
|
|
│ └── secrets.yaml # Encrypted secrets
|
||
|
|
│
|
||
|
|
├── flake.lock # Pinned versions (COMMIT THIS)
|
||
|
|
└── docs/
|
||
|
|
├── README.md # Getting started
|
||
|
|
├── SETUP.md # Detailed setup instructions
|
||
|
|
├── QUICKREF.md # Command reference
|
||
|
|
├── CUSTOMIZATION.md # Extension patterns
|
||
|
|
└── ARCHITECTURE.md # This file
|
||
|
|
```
|
||
|
|
|
||
|
|
## Data Flow
|
||
|
|
|
||
|
|
### On a NixOS System
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────┐
|
||
|
|
│ flake.nix (configuration) │
|
||
|
|
│ - Defines all inputs (nixpkgs, etc) │
|
||
|
|
│ - Specifies nixosConfigurations │
|
||
|
|
└──────────────────┬──────────────────────┘
|
||
|
|
│
|
||
|
|
┌──────────┴──────────┬─────────────────┐
|
||
|
|
│ │ │
|
||
|
|
┌────▼────┐ ┌─────▼──────┐ ┌─────▼──────┐
|
||
|
|
│ hosts/* │ │ nixos/* │ │ home/* │
|
||
|
|
│(device) │ ──────► │(system cfg)│ │(user cfg) │
|
||
|
|
└────┬────┘ └─────┬──────┘ └─────┬──────┘
|
||
|
|
│ │ │
|
||
|
|
└────────────────────┼─────────────────┘
|
||
|
|
│
|
||
|
|
┌────────────▼──────────────┐
|
||
|
|
│ nixos-rebuild switch │
|
||
|
|
│ (applies both system+home)│
|
||
|
|
└───────────┬────────────────┘
|
||
|
|
│
|
||
|
|
┌───────────────────┴───────────────────┐
|
||
|
|
│ │
|
||
|
|
┌────▼──────────────┐ ┌──────────────▼─────┐
|
||
|
|
│ NixOS System │ │ Home Manager │
|
||
|
|
│ /etc/nixos/* │ │ ~/.config/* │
|
||
|
|
│ Services, kernel │ │ Packages, dotfiles │
|
||
|
|
└───────────────────┘ └────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
### On Non-NixOS System
|
||
|
|
|
||
|
|
```
|
||
|
|
┌──────────────────────────────────────┐
|
||
|
|
│ flake.nix (homeConfigurations only) │
|
||
|
|
└──────────────────┬───────────────────┘
|
||
|
|
│
|
||
|
|
┌──────────▼──────────────┐
|
||
|
|
│ home/* (user config) │
|
||
|
|
└──────────┬───────────────┘
|
||
|
|
│
|
||
|
|
┌──────────▼──────────────────┐
|
||
|
|
│ home-manager switch --flake │
|
||
|
|
└──────────┬───────────────────┘
|
||
|
|
│
|
||
|
|
┌──────────▼──────────────────┐
|
||
|
|
│ Home Manager │
|
||
|
|
│ ~/.config, ~/.local/share │
|
||
|
|
│ Packages, dotfiles │
|
||
|
|
└────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
## Module Design Pattern
|
||
|
|
|
||
|
|
Each module follows this pattern for maximum flexibility:
|
||
|
|
|
||
|
|
```nix
|
||
|
|
{ config, lib, pkgs, ... }:
|
||
|
|
|
||
|
|
{
|
||
|
|
# 1. Define options (schema)
|
||
|
|
options.custom.feature = {
|
||
|
|
enable = lib.mkEnableOption "Feature";
|
||
|
|
setting1 = lib.mkOption { /* ... */ };
|
||
|
|
};
|
||
|
|
|
||
|
|
# 2. Conditionally implement
|
||
|
|
config = let
|
||
|
|
cfg = config.custom.feature;
|
||
|
|
in lib.mkIf cfg.enable {
|
||
|
|
# Implementation here
|
||
|
|
environment.systemPackages = [ /* ... */ ];
|
||
|
|
};
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Why this pattern:**
|
||
|
|
- ✅ **Declarative**: Clear what can be configured
|
||
|
|
- ✅ **Composable**: Modules don't interfere
|
||
|
|
- ✅ **Overridable**: Host can override any setting
|
||
|
|
- ✅ **Conditional**: Only included when enabled
|
||
|
|
|
||
|
|
## Separation of Concerns
|
||
|
|
|
||
|
|
### System Level (nixos/) - Run once, affects all users
|
||
|
|
|
||
|
|
- OS-level packages (compilers, tools)
|
||
|
|
- Services (SSH, web servers)
|
||
|
|
- Bootloader, kernel, hardware
|
||
|
|
- User creation and permissions
|
||
|
|
- Firewall, networking
|
||
|
|
- System-wide environment variables
|
||
|
|
|
||
|
|
**When to put config here:**
|
||
|
|
- Affects the entire system
|
||
|
|
- Required by multiple users
|
||
|
|
- Needs root/sudo privileges
|
||
|
|
|
||
|
|
### User Level (home/) - Per-user, portable
|
||
|
|
|
||
|
|
- Shell configuration and aliases
|
||
|
|
- Editor settings and plugins
|
||
|
|
- User-installed packages
|
||
|
|
- Dotfiles (~/.config/*)
|
||
|
|
- Environment variables (user-specific)
|
||
|
|
- Git, SSH client configuration
|
||
|
|
|
||
|
|
**When to put config here:**
|
||
|
|
- Only one user needs it
|
||
|
|
- Doesn't require system privileges
|
||
|
|
- Configurable per-user
|
||
|
|
- Want to port to non-NixOS systems
|
||
|
|
|
||
|
|
## Configuration Hierarchy
|
||
|
|
|
||
|
|
Settings are applied in this order (later overrides earlier):
|
||
|
|
|
||
|
|
1. **nixos/default.nix** - Base system defaults
|
||
|
|
2. **nixos/modules/*.nix** - System feature modules
|
||
|
|
3. **hosts/hostname/default.nix** - Machine-specific overrides
|
||
|
|
4. **home/default.nix** - User base defaults
|
||
|
|
5. **home/modules/*.nix** - User feature modules
|
||
|
|
|
||
|
|
Example with `custom.development.languages`:
|
||
|
|
|
||
|
|
```nix
|
||
|
|
# Start: undefined
|
||
|
|
|
||
|
|
# nixos/modules/development.nix:
|
||
|
|
# options.custom.development.languages = lib.mkOption { default = []; ... };
|
||
|
|
|
||
|
|
# hosts/laptop/default.nix:
|
||
|
|
custom.development.languages = [ "rust" "python" ]; # Override
|
||
|
|
|
||
|
|
# Result: [ "rust" "python" ]
|
||
|
|
```
|
||
|
|
|
||
|
|
## Flakes Architecture
|
||
|
|
|
||
|
|
### Inputs (Dependencies)
|
||
|
|
|
||
|
|
```nix
|
||
|
|
inputs = {
|
||
|
|
nixpkgs = "..."; # Stable packages
|
||
|
|
nixpkgs-unstable = "..."; # Cutting-edge packages
|
||
|
|
home-manager = "..."; # User config management
|
||
|
|
sops-nix = "..."; # Secrets management
|
||
|
|
disko = "..."; # Disk partitioning
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
**Why these inputs:**
|
||
|
|
- **nixpkgs + unstable**: Mix stable (secure) with latest (features)
|
||
|
|
- **home-manager**: User config separate from system
|
||
|
|
- **sops-nix**: Encrypted secrets, portable
|
||
|
|
- **disko**: Declarative disk setup for new machines
|
||
|
|
|
||
|
|
### Outputs (What's available)
|
||
|
|
|
||
|
|
```nix
|
||
|
|
outputs = {
|
||
|
|
nixosConfigurations = {
|
||
|
|
laptop = nixosSystem { ... }; # Full system config
|
||
|
|
server = nixosSystem { ... };
|
||
|
|
};
|
||
|
|
homeConfigurations = {
|
||
|
|
"myuser@linux" = homeManagerConfiguration { ... }; # Standalone HM
|
||
|
|
};
|
||
|
|
devShells = {
|
||
|
|
default = mkShell { ... }; # Development environment
|
||
|
|
};
|
||
|
|
apps = {
|
||
|
|
installer = { ... }; # Bootstrap helper
|
||
|
|
};
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Secrets Flow
|
||
|
|
|
||
|
|
```
|
||
|
|
secrets/
|
||
|
|
├── .sops.yaml # Key configuration (WHO can decrypt)
|
||
|
|
└── secrets.yaml (encrypted)
|
||
|
|
│
|
||
|
|
├─ SSH keys
|
||
|
|
├─ API tokens
|
||
|
|
└─ Passwords
|
||
|
|
|
||
|
|
When applied:
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
Sops decrypts (using ~/.config/sops/age/keys.txt)
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
sops.secrets.* paths available in Nix
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
Placed in /run/secrets/* at boot
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
Referenced in system/home config
|
||
|
|
```
|
||
|
|
|
||
|
|
**Key insight**: Secrets are encrypted on disk, decrypted at boot, never stored in nix store in plaintext.
|
||
|
|
|
||
|
|
## Multi-Machine Scaling
|
||
|
|
|
||
|
|
To support N machines:
|
||
|
|
|
||
|
|
1. Add new host in `hosts/newhost/default.nix`
|
||
|
|
2. Register in `flake.nix` under `nixosConfigurations.newhost`
|
||
|
|
3. Deploy with: `sudo nixos-rebuild switch --flake .#newhost`
|
||
|
|
|
||
|
|
Each machine:
|
||
|
|
- Shares `nixos/` modules
|
||
|
|
- Shares `home/` modules
|
||
|
|
- Has unique `hosts/` overrides
|
||
|
|
- Uses same `secrets.yaml` (all machines can decrypt)
|
||
|
|
|
||
|
|
```
|
||
|
|
Common Base Per-Machine Override
|
||
|
|
───────────── ──────────────────
|
||
|
|
nixos/default.nix → hosts/laptop/default.nix
|
||
|
|
nixos/modules/ → (machine-specific features)
|
||
|
|
home/modules/ → (shared everywhere)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Update Strategy
|
||
|
|
|
||
|
|
```
|
||
|
|
Stable ← └─ Pinned via flake.lock
|
||
|
|
│
|
||
|
|
├─ Low risk, predictable
|
||
|
|
└─ Most system components
|
||
|
|
|
||
|
|
Unstable → Override in modules when needed
|
||
|
|
│
|
||
|
|
├─ Fast-moving
|
||
|
|
└─ For specific packages (bleeding edge editor, tools)
|
||
|
|
```
|
||
|
|
|
||
|
|
Update process:
|
||
|
|
```bash
|
||
|
|
# Pin current state
|
||
|
|
git commit flake.lock
|
||
|
|
|
||
|
|
# Update specific input
|
||
|
|
nix flake update nixpkgs
|
||
|
|
|
||
|
|
# Test
|
||
|
|
sudo nixos-rebuild test --flake .#laptop
|
||
|
|
|
||
|
|
# Deploy
|
||
|
|
sudo nixos-rebuild switch --flake .#laptop
|
||
|
|
|
||
|
|
# Save new state
|
||
|
|
git commit flake.lock
|
||
|
|
```
|
||
|
|
|
||
|
|
## Extension Points
|
||
|
|
|
||
|
|
### Add Feature: Create a new module
|
||
|
|
|
||
|
|
1. Create `nixos/modules/myfeature.nix` or `home/modules/myfeature.nix`
|
||
|
|
2. Follow module pattern (options + config)
|
||
|
|
3. Import in `nixos/default.nix` or `home/default.nix`
|
||
|
|
4. Enable in host config: `custom.myfeature.enable = true`
|
||
|
|
|
||
|
|
### Add Machine: Copy host template
|
||
|
|
|
||
|
|
1. Create `hosts/newmachine/default.nix`
|
||
|
|
2. Customize for that machine
|
||
|
|
3. Add to `flake.nix` under `nixosConfigurations`
|
||
|
|
4. Deploy with `sudo nixos-rebuild switch --flake .#newmachine`
|
||
|
|
|
||
|
|
### Add Dependency: Update flake inputs
|
||
|
|
|
||
|
|
1. Edit `flake.nix` inputs section
|
||
|
|
2. Run `nix flake update`
|
||
|
|
3. Commit `flake.lock`
|
||
|
|
|
||
|
|
### Add Secret: Edit secrets.yaml
|
||
|
|
|
||
|
|
1. Run `sops secrets/secrets.yaml`
|
||
|
|
2. Add key-value pair
|
||
|
|
3. Reference in module with `config.sops.secrets."key".path`
|
||
|
|
|
||
|
|
## Testing & Safety
|
||
|
|
|
||
|
|
### Before Deploying
|
||
|
|
|
||
|
|
```bash
|
||
|
|
nix flake check # Syntax validation
|
||
|
|
nix flake show # Check all outputs
|
||
|
|
sudo nixos-rebuild test # Build without activating
|
||
|
|
```
|
||
|
|
|
||
|
|
### Rollback if Issues
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# NixOS (automatic previous generation)
|
||
|
|
sudo nixos-rebuild --rollback switch
|
||
|
|
|
||
|
|
# Home Manager (manual)
|
||
|
|
home-manager switch --flake .#myuser@linux ~/.local/state/home-manager/previous
|
||
|
|
```
|
||
|
|
|
||
|
|
### Validate Changes
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# What changed?
|
||
|
|
sudo nixos-rebuild diff --flake .#laptop
|
||
|
|
|
||
|
|
# What will happen?
|
||
|
|
sudo nixos-rebuild test --flake .#laptop
|
||
|
|
|
||
|
|
# Apply
|
||
|
|
sudo nixos-rebuild switch --flake .#laptop
|
||
|
|
```
|
||
|
|
|
||
|
|
## Performance Considerations
|
||
|
|
|
||
|
|
- **Flakes**: Slow evaluation (1-2 seconds) but reproducible
|
||
|
|
- **Modularity**: More files but faster edits
|
||
|
|
- **Secrets**: Decryption only at boot (fast)
|
||
|
|
- **Unstable**: More binary cache misses (compilation time)
|
||
|
|
|
||
|
|
## See Also
|
||
|
|
|
||
|
|
- **README.md** - Getting started
|
||
|
|
- **SETUP.md** - Step-by-step installation
|
||
|
|
- **QUICKREF.md** - Common commands
|
||
|
|
- **CUSTOMIZATION.md** - Extension patterns
|