Declarative macOS configuration using nix-darwin, home-manager, Stylix, and Nix flakes.
Fork this repo, create a TOML host file, rebuild. Everything else is shared.
- macOS (Apple Silicon or Intel)
- Nix with flakes enabled
- nix-darwin bootstrapped
- Homebrew installed (nix-darwin manages it declaratively after initial install)
- 1Password for SSH key signing (optional)
# 1. Clone
sudo git clone <repo-url> /etc/nix-darwin
cd /etc/nix-darwin
# 2. Create your host file
cp hosts/wills-macbook-air.toml hosts/my-mac.toml
# Edit hosts/my-mac.toml with your details
# 3. First build (uses darwin-rebuild directly — nh isn't installed yet)
sudo nix flake update --flake /etc/nix-darwin && darwin-rebuild switch --flake /etc/nix-darwinAfter the first build, nh and the rebuild alias are available:
rebuildA setup TUI (M2) is planned to automate host file creation.
.
├── flake.nix # Entry point: auto-discovers hosts/*.toml
├── flake.lock
├── fonts.nix # Font catalog (flat keys)
├── schema.json # JSON Schema for host TOML validation
├── wallpapers/ # Wallpaper collection
│ └── default.avif
├── modules/
│ ├── common.nix # Nix, Stylix, keyboard, macOS defaults (host-driven)
│ ├── packages.nix # CLI tools (base + host extras)
│ ├── homebrew.nix # GUI apps + tools (integration deps + host extras)
│ └── home.nix # Home-manager: conditional dotfile imports
├── hosts/
│ └── *.toml # One per machine (auto-discovered)
└── dotfiles/
├── curated/ # Opinionated configs (behavior + keybinds + LSP)
│ ├── zsh.nix, starship.nix, fzf.nix, bat.nix, btop.nix
│ ├── ghostty.nix, zed.nix, tmux.nix, git.nix
│ └── direnv.nix, zoxide.nix, mise.nix
└── minimal/ # Bare enablement (just programs.X.enable = true)
└── (same files)
hosts/my-mac.toml (per-machine: user, platform, font, theme, programs)
│
└── builtins.fromTOML
│
flake.nix: mkDarwin
├── fonts.nix (font catalog — flat string keys)
├── modules/ (system-level: shared across all hosts)
│ ├── common.nix Stylix (theme + fonts), nix settings, macOS defaults
│ ├── packages.nix CLI tools + host extra_packages.cli
│ └── homebrew.nix GUI apps + host extra_packages.brews/casks
│
└── home.nix (user-level: conditionally imports dotfiles)
└── dotfiles/{curated,minimal}/ (selected per-program from host TOML)
flake.nixreads allhosts/*.tomlviabuiltins.readDir+builtins.fromTOML(auto-discovery, no manual registration).mkDarwinresolves the font key and theme name, then passeshost,font, andthemeNameviaspecialArgs.common.nixconfigures Stylix with the font and theme — Stylix auto-themes all apps.home.nixreadshost.programsand importsdotfiles/curated/<prog>.nixordotfiles/minimal/<prog>.nixfor each enabled program.- Dotfiles contain only behavior config (keybinds, aliases, LSP settings) — no colors or fonts.
#:schema ../schema.json
[machine]
computer_name = "My-MacBook" # must match: scutil --get LocalHostName
type = "apple-silicon" # or "intel"
username = "myuser" # macOS login username
[user]
name = "Your Name" # git commit author
email = "you@example.com" # git commit email
ssh_key = "ssh-ed25519 AAAA..." # public key (optional, for git signing)
[appearance]
font = "monaspace-neon" # see Font System below
theme = "catppuccin-mocha" # base16 scheme name
wallpaper = "default" # name from wallpapers/ dir, or "custom"
[programs]
# Each program: "curated" (opinionated config) or "minimal" (bare enable)
# Omit a program entirely to skip installation.
zsh = "curated"
starship = "curated"
fzf = "curated"
bat = "curated"
btop = "curated"
ghostty = "curated"
zed = "curated"
tmux = "curated"
git = "curated"
direnv = "curated"
zoxide = "curated"
mise = "curated"
[integrations]
ssh_signing = "1password" # or "gpg", omit to disable signing
[extra_packages]
cli = ["ripgrep", "fd", "jq"] # nixpkgs CLI tools
brews = ["awscli"] # Homebrew formulae
casks = ["google-chrome", "slack"] # Homebrew casks (GUI apps)
[macos.keyboard]
caps_lock = "escape" # or "caps-lock"
key_repeat = "fast" # slow / medium / fast
repeat_delay = "short" # long / medium / short
[macos.dock]
position = "bottom" # left / bottom / right
icon_size = "medium" # small / medium / large
auto_hide = true
minimize_to_app = true
[macos.finder]
default_view = "list" # list / columns / icons / gallery
show_extensions = true
show_path_bar = false
show_hidden = true
[macos.trackpad]
tap_to_click = true
natural_scroll = false| Value | What you get |
|---|---|
"curated" |
Opinionated config: keybinds, aliases, LSP settings, integrations |
"minimal" |
Just programs.X.enable = true — works, no opinions |
- Create a TOML file in
hosts/:
cp hosts/wills-macbook-air.toml hosts/my-mac.toml-
Edit the file with your machine details (see Host File Format above).
-
Rebuild — the new host is auto-discovered:
rebuildNo changes to flake.nix required.
Stylix handles all theming automatically. Set a base16 scheme name in your host file:
[appearance]
theme = "catppuccin-mocha" # defaultTo see all available themes:
ls $(nix build nixpkgs#base16-schemes --no-link --print-out-paths)/share/themes/Popular options: catppuccin-mocha, catppuccin-latte, dracula, gruvbox-dark-hard, nord, tokyo-night-dark, solarized-dark.
Stylix themes these apps automatically: bat, btop, fzf, ghostty, starship, tmux, zed.
To disable Stylix for a specific app and theme it manually:
# In the relevant dotfiles/curated/*.nix
stylix.targets.ghostty.enable = false;Fonts are defined in fonts.nix as flat string keys. Each entry has:
| Field | Used by | Example |
|---|---|---|
name |
Editors | MonaspiceNe Nerd Font |
mono |
Terminals | MonaspiceNe Nerd Font Mono |
pkg |
Nix package install | monaspace |
| Key | Font |
|---|---|
0xproto |
0xProto |
cascadia-code |
Cascadia Code |
cascadia-mono |
Cascadia Mono |
comic-shanns |
Comic Shanns Mono |
commit-mono |
Commit Mono |
cousine |
Cousine |
droid-sans-mono |
Droid Sans Mono |
fira-code |
Fira Code |
geist-mono |
Geist Mono |
hack |
Hack |
ibm-plex-mono |
IBM Plex Mono |
inconsolata |
Inconsolata |
iosevka |
Iosevka |
iosevka-term |
Iosevka Term |
jetbrains-mono |
JetBrains Mono |
jetbrains-mono-no-liga |
JetBrains Mono (no ligatures) |
lilex |
Lilex |
meslo |
Meslo LG S |
monaspace-neon |
Monaspace Neon (default) |
monaspace-argon |
Monaspace Argon |
monaspace-xenon |
Monaspace Xenon |
monaspace-krypton |
Monaspace Krypton |
monaspace-radon |
Monaspace Radon |
mononoki |
Mononoki |
noto |
Noto Sans Mono |
roboto-mono |
Roboto Mono |
source-code-pro |
Source Code Pro |
ubuntu-mono |
Ubuntu Mono |
victor-mono |
Victor Mono |
zed-mono |
Zed Mono |
Set the key in your host file:
[appearance]
font = "jetbrains-mono"# Find the nix package name
nix eval nixpkgs#nerd-fonts --apply 'x: builtins.attrNames x'
# Check the registered font family name
ghostty +list-fonts | grep -i <name>Add to fonts.nix:
"my-font" = { name = "MyFont Nerd Font"; mono = "MyFont Nerd Font Mono"; pkg = "my-font"; };Then use "my-font" as the font key in any host TOML.
- Create
dotfiles/curated/myapp.nix(opinionated config):
{ ... }:
{
programs.myapp = {
enable = true;
# behavior config here — Stylix handles theme/font
};
}- Create
dotfiles/minimal/myapp.nix(bare enablement):
{ ... }:
{
programs.myapp.enable = true;
}- Add
myappto theprogramsenum inschema.json:
"myapp": { "type": "string", "enum": ["curated", "minimal"], "description": "..." }home.nixwill now pick upprograms.myapp = "curated"or"minimal"from host TOML files automatically.
If the app is installed via Homebrew, set package = null in the dotfile.
| Where | What goes here | Examples |
|---|---|---|
[programs] (host TOML) |
Config-managed tools with curated/minimal dotfiles | zsh, git, tmux, ghostty, zed |
extra_packages.cli (host TOML) |
Per-host CLI tools from nixpkgs | ripgrep, fd, jq, kubectl |
extra_packages.brews (host TOML) |
Per-host Homebrew formulae | awscli, mise |
extra_packages.casks (host TOML) |
Per-host GUI apps | Chrome, Slack, Zed, Discord |
All packages are per-host via your TOML file. The only shared base package is nh (the rebuild helper).
Rule of thumb: If a tool has rich config, add it as a program with a dotfile. GUI or slow-to-build goes to Homebrew casks. Everything else goes to extra_packages.cli (nixpkgs).
| Binding | Action |
|---|---|
\ |
Split right |
_ |
Split down |
h/j/k/l |
Navigate panes |
H/J/K/L |
Resize panes |
n/p |
Next/previous window |
S |
Choose session |
| Binding | Action |
|---|---|
ctrl-h/j/k/l |
Navigate panes |
shift-h/l |
Previous/next tab |
space v |
Split right |
space s |
Split down |
| Binding | Action |
|---|---|
cmd+shift+enter |
Split right |
cmd+shift+- |
Split down |
cmd+opt+arrows |
Navigate splits |
rebuild # Rebuild (alias)
ghostty +list-fonts | grep -i <name> # Check font availability
nix-collect-garbage --delete-older-than 7d # Manual GC (runs weekly automatically)
statix check . # Lint nix files
deadnix . # Find unused code
nixfmt . # Format nix files