37 KiB
Nix Home Manager Flake Architecture
Overview
This document defines the architecture for migrating from chezmoi to Nix Home Manager with flakes. The design supports two target environments (personal and work) while maintaining a clean, modular, and extensible structure.
1. Directory Structure
.
├── flake.nix # Main flake entry point
├── flake.lock # Locked dependencies
├── README.md # Setup and usage documentation
│
├── hosts/ # Machine-specific configurations
│ ├── personal/
│ │ └── default.nix # Personal machine darwin config
│ └── work/
│ └── default.nix # Work machine darwin config
│
├── home/ # Home Manager configurations
│ ├── default.nix # Shared home configuration
│ ├── personal.nix # Personal profile overrides
│ └── work.nix # Work profile overrides
│
├── modules/ # Reusable Nix modules
│ ├── darwin/ # macOS-specific modules
│ │ ├── default.nix # Common darwin settings
│ │ ├── homebrew.nix # Homebrew cask management
│ │ └── system.nix # System preferences
│ │
│ └── home/ # Home Manager modules
│ ├── shell/
│ │ ├── default.nix # Shell module entry point
│ │ ├── zsh.nix # Zsh configuration
│ │ ├── starship.nix # Starship prompt
│ │ ├── atuin.nix # Shell history
│ │ ├── direnv.nix # Directory environments
│ │ └── aliases.nix # Shell aliases
│ │
│ ├── git/
│ │ ├── default.nix # Git module entry point
│ │ ├── config.nix # Core git configuration
│ │ └── identities.nix # Git identity management
│ │
│ ├── ssh/
│ │ ├── default.nix # SSH module entry point
│ │ └── hosts.nix # SSH host configurations
│ │
│ ├── terminal/
│ │ ├── default.nix # Terminal module entry point
│ │ ├── tmux.nix # Tmux configuration
│ │ └── neovim.nix # Neovim configuration
│ │
│ ├── packages/
│ │ ├── default.nix # Package module entry point
│ │ ├── cli.nix # CLI tools
│ │ ├── development.nix # Development tools
│ │ └── kubernetes.nix # K8s tools
│ │
│ └── apps/
│ ├── default.nix # Apps module entry point
│ └── aerospace.nix # Window manager config
│
├── lib/ # Helper functions
│ └── default.nix # Utility functions
│
├── overlays/ # Nixpkgs overlays
│ └── default.nix # Custom package overlays
│
├── secrets/ # Encrypted secrets (sops-nix)
│ ├── secrets.yaml # Encrypted secrets file
│ └── .sops.yaml # SOPS configuration
│
└── docs/ # Documentation
├── migration-analysis.md # Migration analysis
└── nix-architecture.md # This document
Design Rationale
-
hosts/- Machine-level configurations using nix-darwin. Each host can have unique system settings while sharing common darwin modules. -
home/- Profile-specific home-manager configurations. Thedefault.nixcontains shared settings, whilepersonal.nixandwork.nixcontain profile-specific overrides. -
modules/- Reusable, composable modules organized by functionality. Split intodarwin/(system-level) andhome/(user-level) to maintain clear separation. -
lib/- Helper functions for reducing boilerplate and improving maintainability. -
overlays/- Custom package modifications or additions to nixpkgs. -
secrets/- Encrypted secrets managed by sops-nix for sensitive data like API tokens.
2. Flake Structure
2.1 Inputs
{
inputs = {
# Core dependencies
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
# Home Manager for user environment
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
# nix-darwin for macOS system configuration
nix-darwin = {
url = "github:LnL7/nix-darwin";
inputs.nixpkgs.follows = "nixpkgs";
};
# Secrets management
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
# Optional: Homebrew management through nix-darwin
nix-homebrew = {
url = "github:zhaofengli-wip/nix-homebrew";
};
};
}
2.2 Outputs Structure
{
outputs = { self, nixpkgs, home-manager, nix-darwin, sops-nix, nix-homebrew, ... }@inputs:
let
# Supported systems (macOS for now, Linux can be added later)
systems = [ "aarch64-darwin" "x86_64-darwin" ];
# Helper to generate per-system attributes
forAllSystems = nixpkgs.lib.genAttrs systems;
# Common special args passed to all modules
specialArgs = { inherit inputs; };
in
{
# Darwin (macOS) system configurations
darwinConfigurations = {
# Personal machine configuration
"personal" = nix-darwin.lib.darwinSystem {
system = "aarch64-darwin";
inherit specialArgs;
modules = [
./hosts/personal
./modules/darwin
home-manager.darwinModules.home-manager
{
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = specialArgs;
users.alice = { ... }: {
imports = [
./home
./home/personal.nix
];
};
};
}
];
};
# Work machine configuration
"work" = nix-darwin.lib.darwinSystem {
system = "aarch64-darwin";
inherit specialArgs;
modules = [
./hosts/work
./modules/darwin
home-manager.darwinModules.home-manager
{
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = specialArgs;
users.alice = { ... }: {
imports = [
./home
./home/work.nix
];
};
};
}
];
};
};
# Standalone Home Manager configurations (for non-darwin systems)
homeConfigurations = {
"personal@linux" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
extraSpecialArgs = specialArgs;
modules = [
./home
./home/personal.nix
];
};
"work@linux" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
extraSpecialArgs = specialArgs;
modules = [
./home
./home/work.nix
];
};
};
# Development shells for this repository
devShells = forAllSystems (system:
let pkgs = nixpkgs.legacyPackages.${system};
in {
default = pkgs.mkShell {
packages = with pkgs; [ nixfmt sops age ];
};
}
);
};
}
2.3 Key Design Decisions
-
nix-darwin as primary entry point - On macOS, use
darwinConfigurationswhich integrates home-manager as a module. This allows managing both system-level settings (Homebrew, system preferences) and user-level dotfiles in one command. -
Standalone homeConfigurations - Provided for future Linux support or cases where nix-darwin isn't desired.
-
useGlobalPkgsanduseUserPackages- Ensures home-manager uses the same nixpkgs instance as darwin, avoiding duplicate package downloads. -
specialArgs- Passes inputs to all modules, enabling access to flake inputs anywhere in the configuration.
3. Module Design
3.1 Module Architecture Diagram
graph TB
subgraph Flake
F[flake.nix]
end
subgraph Darwin Configs
DP[hosts/personal]
DW[hosts/work]
end
subgraph Darwin Modules
DM[modules/darwin/default.nix]
HB[modules/darwin/homebrew.nix]
SYS[modules/darwin/system.nix]
end
subgraph Home Configs
HC[home/default.nix]
HP[home/personal.nix]
HW[home/work.nix]
end
subgraph Home Modules
SH[modules/home/shell]
GIT[modules/home/git]
SSH[modules/home/ssh]
TM[modules/home/terminal]
PKG[modules/home/packages]
APP[modules/home/apps]
end
F --> DP
F --> DW
DP --> DM
DW --> DM
DM --> HB
DM --> SYS
DP --> HC
DW --> HC
DP --> HP
DW --> HW
HC --> SH
HC --> GIT
HC --> SSH
HC --> TM
HC --> PKG
HC --> APP
3.2 Shared vs Profile-Specific Content
| Component | Shared | Personal-Specific | Work-Specific |
|---|---|---|---|
| Shell | zsh, starship, atuin, aliases | - | - |
| Git | Core config, aliases, delta | Personal signing key, personal email | Work signing key, work email, zeronorth config |
| SSH | 1Password agent, control master | github-private host | github-zeronorth host |
| Packages | CLI tools, dev tools, k8s | Personal apps (darktable, proton, steam) | - |
| Homebrew Casks | 1password, ghostty, raycast | signal, proton-*, steam | - |
| Project Configs | - | ~/Projects/.gitconfig | ~/Projects/private/.gitconfig, ~/Projects/zeronorth/.gitconfig |
3.3 Module Option Pattern
Each module should follow this pattern for configurability:
# modules/home/git/default.nix
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.modules.git;
in
{
options.modules.git = {
enable = mkEnableOption "Git configuration";
userName = mkOption {
type = types.str;
default = "Morten Olsen";
description = "Git user name";
};
userEmail = mkOption {
type = types.str;
description = "Git user email";
};
signing = {
enable = mkEnableOption "Git commit signing";
key = mkOption {
type = types.str;
description = "SSH signing key";
};
};
includes = mkOption {
type = types.listOf (types.submodule {
options = {
condition = mkOption { type = types.str; };
path = mkOption { type = types.str; };
};
});
default = [];
description = "Conditional git config includes";
};
};
config = mkIf cfg.enable {
programs.git = {
enable = true;
userName = cfg.userName;
userEmail = cfg.userEmail;
signing = mkIf cfg.signing.enable {
key = cfg.signing.key;
signByDefault = true;
};
extraConfig = {
gpg.format = "ssh";
gpg.ssh.program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign";
# ... other shared config
};
includes = cfg.includes;
};
};
}
3.4 Module Implementations
Shell Module (modules/home/shell/default.nix)
{ config, lib, pkgs, ... }:
{
imports = [
./zsh.nix
./starship.nix
./atuin.nix
./direnv.nix
./aliases.nix
];
options.modules.shell = {
enable = lib.mkEnableOption "Shell configuration";
};
config = lib.mkIf config.modules.shell.enable {
# Enable all shell sub-modules
modules.shell.zsh.enable = true;
modules.shell.starship.enable = true;
modules.shell.atuin.enable = true;
modules.shell.direnv.enable = true;
};
}
Git Module (modules/home/git/default.nix)
{ config, lib, pkgs, ... }:
{
imports = [
./config.nix
./identities.nix
];
options.modules.git = {
enable = lib.mkEnableOption "Git configuration";
profile = lib.mkOption {
type = lib.types.enum [ "personal" "work" ];
description = "Git profile to use";
};
};
}
SSH Module (modules/home/ssh/default.nix)
{ config, lib, pkgs, ... }:
{
imports = [ ./hosts.nix ];
options.modules.ssh = {
enable = lib.mkEnableOption "SSH configuration";
onePasswordAgent = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Use 1Password SSH agent";
};
};
config = lib.mkIf config.modules.ssh.enable {
programs.ssh = {
enable = true;
controlMaster = "auto";
controlPath = "/tmp/ssh-%r@%h:%p";
forwardAgent = true;
extraConfig = lib.mkIf config.modules.ssh.onePasswordAgent ''
IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
'';
includes = [ "~/.colima/ssh_config" ];
};
};
}
Homebrew Module (modules/darwin/homebrew.nix)
{ config, lib, pkgs, ... }:
let
cfg = config.modules.homebrew;
in
{
options.modules.homebrew = {
enable = lib.mkEnableOption "Homebrew management";
personalCasks = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Include personal-only casks";
};
};
config = lib.mkIf cfg.enable {
homebrew = {
enable = true;
onActivation = {
autoUpdate = true;
cleanup = "zap";
upgrade = true;
};
taps = [
"nikitabobko/tap"
"coder/coder"
"fluxcd/tap"
"sst/tap"
];
casks = [
"1password"
"1password-cli"
"aerospace"
"dbeaver-community"
"ghostty"
"home-assistant"
"jellyfin-media-player"
"lens"
"localsend"
"mqtt-explorer"
"obsidian"
"ollama-app"
"raycast"
] ++ lib.optionals cfg.personalCasks [
"darktable"
"signal"
"proton-mail-bridge"
"proton-pass"
"protonvpn"
"steam"
];
};
};
}
3.5 Secrets Management
For sensitive data, use sops-nix with age encryption:
# secrets/.sops.yaml
keys:
- &personal age1... # Personal machine key
- &work age1... # Work machine key
creation_rules:
- path_regex: secrets/personal\.yaml$
key_groups:
- age:
- *personal
- path_regex: secrets/work\.yaml$
key_groups:
- age:
- *work
- path_regex: secrets/secrets\.yaml$
key_groups:
- age:
- *personal
- *work
For 1Password integration (already used in current setup), reference secrets directly:
# In modules that need secrets
home.sessionVariables = {
NODE_AUTH_TOKEN = "op://Employee/Github NPM Token/password";
};
4. Configuration Profiles
4.1 Shared Configuration (home/default.nix)
{ config, lib, pkgs, ... }:
{
imports = [
../modules/home/shell
../modules/home/git
../modules/home/ssh
../modules/home/terminal
../modules/home/packages
../modules/home/apps
];
home = {
stateVersion = "24.05";
sessionVariables = {
EDITOR = "nvim";
LANG = "en_US.UTF-8";
};
sessionPath = [
"$HOME/.local/bin"
"$HOME/.cargo/bin"
"/opt/homebrew/bin"
];
};
# Enable shared modules
modules = {
shell.enable = true;
git.enable = true;
ssh.enable = true;
terminal.enable = true;
packages.enable = true;
apps.enable = true;
};
}
4.2 Personal Profile (home/personal.nix)
{ config, lib, pkgs, ... }:
{
# Git configuration for personal
modules.git = {
profile = "personal";
userEmail = "fbtijfdq@void.black";
signing.key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFaIAP/ZJ7+7jeR44e1yIJjfQAB6MN351LDKJAXVF62P";
includes = [
{
condition = "gitdir:~/Projects/";
path = "~/Projects/.gitconfig";
}
];
};
# SSH hosts for personal
modules.ssh.hosts = {
github-private = {
hostname = "ssh.github.com";
user = "git";
port = 443;
identityFile = "~/.ssh/keys/github-private.pub";
};
gitea-ssh-olsen-cloud = {
hostname = "gitea-ssh.olsen.cloud";
user = "git";
port = 2202;
identityFile = "~/.ssh/keys/github-private.pub";
};
};
# Personal-only packages
modules.packages.personal = true;
# Project-specific git config
home.file.".Projects/.gitconfig".text = ''
[user]
email = fbtijfdq@void.black
name = Morten Olsen
signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFaIAP/ZJ7+7jeR44e1yIJjfQAB6MN351LDKJAXVF62P
[commit]
gpgsign = true
[gpg]
format = ssh
[gpg "ssh"]
program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"
[url "ssh://git@ssh-gitea.olsen.cloud:2205/"]
insteadOf = "https://gitea.olsen.cloud/"
[url "git@github-private:"]
insteadOf = https://github.com/
'';
}
4.3 Work Profile (home/work.nix)
{ config, lib, pkgs, ... }:
{
# Git configuration for work
modules.git = {
profile = "work";
userEmail = "fbtijfdq@void.black"; # Default email
signing.key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAzuPy7D/54GxMq9Zhz0CUjaDnEQ6RkQ/yqVYl7U55k";
includes = [
{
condition = "gitdir:~/Projects/private/";
path = "~/Projects/private/.gitconfig";
}
{
condition = "gitdir:~/Projects/zeronorth/";
path = "~/Projects/zeronorth/.gitconfig";
}
];
};
# SSH hosts for work
modules.ssh.hosts = {
github-private = {
hostname = "ssh.github.com";
user = "git";
port = 443;
identityFile = "~/.ssh/keys/github-private.pub";
};
github-zeronorth = {
hostname = "ssh.github.com";
user = "git";
port = 443;
identityFile = "~/.ssh/keys/github-zeronorth.pub";
};
gitea-ssh-olsen-cloud = {
hostname = "gitea-ssh.olsen.cloud";
user = "git";
port = 2202;
identityFile = "~/.ssh/keys/github-private.pub";
};
};
# Work-only packages (none currently)
modules.packages.work = true;
# Project-specific git configs
home.file = {
"Projects/private/.gitconfig".text = ''
[user]
email = fbtijfdq@void.black
name = Morten Olsen
signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAzuPy7D/54GxMq9Zhz0CUjaDnEQ6RkQ/yqVYl7U55k
[commit]
gpgsign = true
[gpg]
format = ssh
[gpg "ssh"]
program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"
[url "ssh://git@ssh-gitea.olsen.cloud:2205/"]
insteadOf = "https://gitea.olsen.cloud/"
[url "git@github-private:"]
insteadOf = https://github.com/
'';
"Projects/zeronorth/.gitconfig".text = ''
[user]
email = morten.olsen@zeronorth.com
name = Morten Olsen
signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKDbZITpz5QrVIxPn9gKVWMPK+3W3YZZGszFOQvO/h7M
[commit]
gpgsign = true
[gpg]
format = ssh
[gpg "ssh"]
program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"
[url "git@github-zeronorth:"]
insteadOf = https://github.com/
'';
"Projects/zeronorth/.envrc".text = ''
export NODE_AUTH_TOKEN="op://Employee/Github NPM Token/password"
export NPM_TOKEN="op://Employee/Github NPM Token/password"
export NPM_GITHUB_TOKEN="op://Employee/Github NPM Token/password"
export AWS_PROFILE=zntest
'';
};
}
4.4 Profile Selection Flow
flowchart TD
A[New Machine Setup] --> B{Machine Type?}
B -->|Personal| C[darwin-rebuild switch --flake .#personal]
B -->|Work| D[darwin-rebuild switch --flake .#work]
C --> E[Loads hosts/personal + home/personal.nix]
D --> F[Loads hosts/work + home/work.nix]
E --> G[Personal SSH keys, git config, packages]
F --> H[Work SSH keys, git config, zeronorth setup]
G --> I[System Ready]
H --> I
5. Usage Patterns
5.1 Initial Setup on New Machine
Prerequisites
- Install Nix with flakes enabled:
curl -L https://nixos.org/nix/install | sh
- Enable flakes in Nix configuration:
mkdir -p ~/.config/nix
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
Personal Machine Setup
# Clone the dotfiles repository
git clone https://github.com/username/dotfiles.git ~/.dotfiles
cd ~/.dotfiles
# Build and activate the personal configuration
nix build .#darwinConfigurations.personal.system
./result/sw/bin/darwin-rebuild switch --flake .#personal
# Subsequent rebuilds
darwin-rebuild switch --flake .#personal
Work Machine Setup
# Clone the dotfiles repository
git clone https://github.com/username/dotfiles.git ~/.dotfiles
cd ~/.dotfiles
# Build and activate the work configuration
nix build .#darwinConfigurations.work.system
./result/sw/bin/darwin-rebuild switch --flake .#work
# Subsequent rebuilds
darwin-rebuild switch --flake .#work
5.2 Switching Configurations
If you need to switch a machine from personal to work (or vice versa):
# Switch to work configuration
darwin-rebuild switch --flake .#work
# Switch back to personal configuration
darwin-rebuild switch --flake .#personal
Note: Switching profiles will change git configs, SSH hosts, and installed packages. Some manual cleanup may be needed for files that were created by the previous profile.
5.3 Updating Configuration
After Editing Nix Files
# Rebuild and switch to new configuration
darwin-rebuild switch --flake .#personal # or .#work
# Build without switching (for testing)
darwin-rebuild build --flake .#personal
Updating Flake Inputs
# Update all inputs
nix flake update
# Update specific input
nix flake lock --update-input nixpkgs
nix flake lock --update-input home-manager
# Rebuild after update
darwin-rebuild switch --flake .#personal
5.4 Common Commands Reference
| Command | Description |
|---|---|
darwin-rebuild switch --flake .#personal |
Apply personal configuration |
darwin-rebuild switch --flake .#work |
Apply work configuration |
darwin-rebuild build --flake .#personal |
Build without applying |
nix flake update |
Update all flake inputs |
nix flake check |
Validate flake configuration |
nix repl --expr 'builtins.getFlake "."' |
Interactive debugging |
darwin-rebuild --rollback |
Rollback to previous generation |
nix-collect-garbage -d |
Clean up old generations |
5.5 Development Workflow
# Enter development shell with tools for editing this repo
nix develop
# Format Nix files
nixfmt **/*.nix
# Check configuration validity
nix flake check
6. Future Extensibility
6.1 Adding Linux Support
The architecture already includes placeholder homeConfigurations for Linux. To fully support Linux:
- Add Linux-specific modules in
modules/home/with platform conditionals:
{ config, lib, pkgs, ... }:
{
config = lib.mkMerge [
# Common configuration
{ ... }
# macOS-specific
(lib.mkIf pkgs.stdenv.isDarwin { ... })
# Linux-specific
(lib.mkIf pkgs.stdenv.isLinux { ... })
];
}
-
Create Linux host configurations in
hosts/linux/. -
Use
homeConfigurationsfor standalone home-manager on Linux.
6.2 Adding New Machines
To add a new machine configuration:
- Create a new directory under
hosts/:
hosts/
├── personal/
├── work/
└── new-machine/
└── default.nix
- Add the configuration to
flake.nix:
darwinConfigurations."new-machine" = nix-darwin.lib.darwinSystem {
# ...
};
6.3 Adding New Modules
To add a new module (e.g., for a new application):
- Create the module file:
modules/home/apps/new-app.nix
- Import it in the parent module:
# modules/home/apps/default.nix
imports = [
./aerospace.nix
./new-app.nix
];
- Add options and configuration following the established pattern.
7. Migration Checklist
When implementing this architecture, follow this order:
- Create basic flake.nix with inputs
- Set up directory structure
- Implement darwin modules (homebrew, system)
- Implement shell modules (zsh, starship, atuin)
- Implement git modules with profile support
- Implement SSH modules
- Implement terminal modules (tmux)
- Implement package modules
- Implement apps modules (aerospace)
- Create personal profile configuration
- Create work profile configuration
- Set up secrets management (sops-nix)
- Test on personal machine
- Test on work machine
- Document any manual steps
- Remove chezmoi after successful migration
8. Summary
This architecture provides:
-
Clean separation of concerns - Darwin system config, home-manager user config, and reusable modules are clearly separated.
-
Profile-based configuration - Personal and work environments are distinct profiles that share common modules but override specific settings.
-
Modular design - Each functional area (shell, git, ssh, packages) is a self-contained module with configurable options.
-
macOS-first with extensibility - Designed for macOS with nix-darwin, but structured to easily add Linux support later.
-
Declarative package management - CLI tools via nixpkgs, GUI apps via Homebrew casks managed through nix-darwin.
-
Secrets handling - Support for both sops-nix encrypted secrets and 1Password CLI references.
-
Simple usage patterns - Single command to apply configuration, easy switching between profiles.
The migration from chezmoi to this Nix-based setup will provide reproducible, declarative configuration management with the full power of the Nix ecosystem.
9. Final Implementation
This section documents the actual implemented structure, which may differ slightly from the original design above.
9.1 Implemented Directory Structure
.
├── flake.nix # Main flake entry point
├── flake.lock # Locked dependencies
├── README.md # Setup and usage documentation
│
├── home/ # Home Manager configurations
│ ├── default.nix # Shared home configuration
│ ├── personal.nix # Personal profile overrides
│ └── work.nix # Work profile overrides
│
├── hosts/ # Machine-specific nix-darwin configs
│ ├── personal/
│ │ └── default.nix # Personal machine darwin settings
│ └── work/
│ └── default.nix # Work machine darwin settings
│
├── modules/ # Reusable Nix modules
│ ├── darwin/
│ │ └── homebrew.nix # Homebrew cask management
│ │
│ └── home/ # Home Manager modules
│ ├── apps.nix # Application configs (aerospace, jellyfin-tui)
│ ├── git.nix # Git configuration with signing
│ ├── git-files.nix # Project-specific git configs
│ ├── packages.nix # CLI packages via Nix
│ ├── shell.nix # Shell environment (zsh, starship, etc.)
│ ├── ssh.nix # SSH configuration with 1Password
│ └── tmux.nix # Tmux configuration
│
└── docs/ # Documentation
├── migration-analysis.md # Original chezmoi analysis
├── nix-architecture.md # This document
└── usage.md # Detailed usage guide
9.2 Module Reference
Darwin Modules
| Module | File | Purpose |
|---|---|---|
| Homebrew | modules/darwin/homebrew.nix |
Manages Homebrew taps, formulae, and casks declaratively |
Home Manager Modules
| Module | File | Purpose |
|---|---|---|
| Shell | modules/home/shell.nix |
Zsh, Starship, Atuin, Direnv, Zoxide, FZF, Pyenv |
| Packages | modules/home/packages.nix |
CLI tools and development packages |
| Git | modules/home/git.nix |
Git configuration with delta, signing, aliases |
| Git Files | modules/home/git-files.nix |
Project-specific gitconfig files |
| SSH | modules/home/ssh.nix |
SSH with 1Password agent, host configurations |
| Tmux | modules/home/tmux.nix |
Tmux with vim navigation, plugins |
| Apps | modules/home/apps.nix |
Aerospace window manager, Jellyfin TUI |
9.3 Module Configuration Options
modules.homebrew (Darwin)
| Option | Type | Default | Description |
|---|---|---|---|
enable |
bool | false |
Enable Homebrew management |
casks.shared |
list of str | (see module) | Casks installed on all machines |
casks.personal |
list of str | (see module) | Casks installed only on personal machines |
casks.enablePersonal |
bool | false |
Whether to install personal casks |
brews |
list of str | (see module) | Homebrew formulae to install |
taps |
list of str | (see module) | Homebrew taps to add |
cleanup |
enum | "zap" |
Cleanup behavior: "none", "uninstall", "zap" |
modules.ssh (Home)
| Option | Type | Default | Description |
|---|---|---|---|
enable |
bool | false |
Enable SSH configuration |
enableGitHubPrivate |
bool | false |
Enable github-private host |
enableGitHubZeronorth |
bool | false |
Enable github-zeronorth host |
enableCoder |
bool | false |
Enable Coder SSH hosts |
enableGiteaPrivate |
bool | false |
Enable gitea-ssh.olsen.cloud host |
githubPrivateKeyPath |
str | "~/.ssh/keys/github-private.pub" |
Path to GitHub private key |
githubZeronorthKeyPath |
str | "~/.ssh/keys/github-zeronorth.pub" |
Path to GitHub Zeronorth key |
modules.git (Home)
| Option | Type | Default | Description |
|---|---|---|---|
enable |
bool | false |
Enable Git configuration |
userName |
str | "Morten Olsen" |
Git user name |
userEmail |
str | (required) | Git user email |
signingKey |
str | (required) | SSH signing key (public key) |
includes |
list of {condition, path} | [] |
Conditional includes for project configs |
modules.gitFiles (Home)
| Option | Type | Default | Description |
|---|---|---|---|
enable |
bool | false |
Enable project-specific git files |
personal.enable |
bool | false |
Create ~/Projects/.gitconfig |
personal.email |
str | (example) | Email for personal projects |
personal.signingKey |
str | (required) | Signing key for personal projects |
private.enable |
bool | false |
Create ~/Projects/private/.gitconfig |
private.email |
str | (example) | Email for private projects |
private.signingKey |
str | (required) | Signing key for private projects |
zeronorth.enable |
bool | false |
Create ~/Projects/zeronorth/.gitconfig |
zeronorth.email |
str | (example) | Email for zeronorth projects |
zeronorth.signingKey |
str | (required) | Signing key for zeronorth projects |
modules.apps (Home)
| Option | Type | Default | Description |
|---|---|---|---|
enable |
bool | false |
Enable application configurations |
aerospace.enable |
bool | true |
Enable Aerospace window manager config |
jellyfin-tui.enable |
bool | false |
Enable Jellyfin TUI config |
jellyfin-tui.serverUrl |
str | "" |
Jellyfin server URL |
jellyfin-tui.serverName |
str | "Home Server" |
Display name for server |
jellyfin-tui.username |
str | "" |
Jellyfin username |
jellyfin-tui.passwordFile |
str | "" |
Path to password file |
9.4 Profile Configuration Summary
Personal Profile (home/personal.nix)
modules.git = {
enable = true;
userEmail = "alice@personal.example.com";
signingKey = "ssh-ed25519 AAAAC3...";
includes = [{ condition = "gitdir:~/Projects/"; path = "~/Projects/.gitconfig"; }];
};
modules.gitFiles = {
enable = true;
personal = { enable = true; email = "..."; signingKey = "..."; };
};
modules.ssh = {
enableGitHubPrivate = true;
enableGiteaPrivate = true;
enableGitHubZeronorth = false;
enableCoder = false;
};
modules.apps = {
jellyfin-tui = { enable = true; serverUrl = "..."; username = "..."; };
};
Work Profile (home/work.nix)
modules.git = {
enable = true;
userEmail = "alice@work.example.com";
signingKey = "ssh-ed25519 AAAAC3...";
includes = [
{ condition = "gitdir:~/Projects/private/"; path = "~/Projects/private/.gitconfig"; }
{ condition = "gitdir:~/Projects/zeronorth/"; path = "~/Projects/zeronorth/.gitconfig"; }
];
};
modules.gitFiles = {
enable = true;
private = { enable = true; email = "..."; signingKey = "..."; };
zeronorth = { enable = true; email = "..."; signingKey = "..."; };
};
modules.ssh = {
enableGitHubPrivate = true;
enableGitHubZeronorth = true;
enableCoder = true;
enableGiteaPrivate = false;
};
9.5 Packages Installed
Via Nix (from modules/home/packages.nix)
| Category | Packages |
|---|---|
| Modern CLI | bat, eza, fd, ripgrep, delta |
| File Utils | jq, yq, tree, rsync, unzip, curl, wget, watch |
| Git | git, gh, jujutsu, lazygit |
| Development | neovim, tmux, nodejs_22, deno, rustup, python313, uv, gnumake, cmake |
| Containers | docker, docker-buildx, docker-compose, colima |
| Kubernetes | kubectl, kubernetes-helm, helmfile, k9s, istioctl, fluxcd |
| Infrastructure | terraform, ansible, sshpass, awscli2 |
| Media | ffmpeg |
| Security | gnupg, age, sops |
| Misc | graphviz, tree-sitter, htop, ncdu, tldr, nixfmt-rfc-style, nil |
Via Homebrew Casks (from modules/darwin/homebrew.nix)
Shared (all machines):
- 1password, 1password-cli
- ghostty, dbeaver-community, lens
- aerospace
- raycast, obsidian
- jellyfin-media-player, ollama-app
- localsend, mqtt-explorer, home-assistant
Personal only:
- darktable
- proton-mail-bridge, proton-pass, protonvpn
- signal
- steam
Via Homebrew Formulae
- coder/coder/coder
- fluxcd/tap/flux
- sst/tap/opencode
- tree-sitter-cli
9.6 Key Differences from Original Design
-
Simplified module structure - Instead of deeply nested modules (e.g.,
modules/home/shell/zsh.nix), the implementation uses flatter files (e.g.,modules/home/shell.nix) for simplicity. -
No sops-nix - Secrets are managed via 1Password CLI references rather than sops-nix encrypted files.
-
No lib/ directory - Helper functions are defined inline in modules rather than in a separate lib directory.
-
No overlays/ - No custom package overlays are currently needed.
-
Combined shell module - All shell-related configuration (zsh, starship, atuin, direnv, zoxide, fzf, pyenv) is in a single
shell.nixfile. -
Separate git-files module - Project-specific gitconfig files are managed in a dedicated
git-files.nixmodule rather than inline in profile files.
9.7 Migration Status
The following chezmoi files have been migrated:
| Chezmoi File | Nix Module | Status |
|---|---|---|
dot_zshrc |
modules/home/shell.nix |
✅ Migrated |
dot_tmux.conf |
modules/home/tmux.nix |
✅ Migrated |
dot_gitconfig.tmpl |
modules/home/git.nix |
✅ Migrated |
dot_gitignore_global |
modules/home/git.nix |
✅ Migrated |
dot_Brewfile.tmpl |
modules/darwin/homebrew.nix + modules/home/packages.nix |
✅ Migrated |
dot_aerospace.toml |
modules/home/apps.nix |
✅ Migrated |
dot_config/atuin/config.toml |
modules/home/shell.nix |
✅ Migrated |
dot_shellrc/rc.d/* |
modules/home/shell.nix |
✅ Migrated |
dot_ssh/config |
modules/home/ssh.nix |
✅ Migrated |
dot_ssh/config.d/* |
modules/home/ssh.nix |
✅ Migrated |
private_Projects/* |
modules/home/git-files.nix |
✅ Migrated |
private_Library/.../jellyfin-tui/* |
modules/home/apps.nix |
✅ Migrated |
The original chezmoi files are kept in the repository for reference during the transition period.