Files
README.md

dotfiles.nix #

Personal macOS environment — fully declarative via nix-darwin + Home Manager — plus the homeserver, which is a NixOS system defined in the same flake. This is not a reusable framework or template. It's my machine's source of truth, published for my own reference and for friends who ask "how do you manage your Mac?"

What it does #

One command (darwin-rebuild switch) materializes:

  • Shell — bash with ble.sh line editor, starship prompt, atuin history, fzf, carapace completions
  • Packages — all CLI tools via nix (ripgrep, bat, eza, jjui, delta, jq, tmux, …), brew as fallback
  • GUI apps — installed and updated via brew casks (Ghostty, Blender, GIMP, Chromium, …)
  • Configs — every dotfile declared in Nix: bash, zsh, git, ssh, starship, atuin, karabiner, hammerspoon, zed, jjui, ghostty, inputrc, herdr
  • Herdr plugins — nix derivations for sysmon and jj-workspace, symlinked into herdr's plugin directory
  • Agent — oh-my-pi config, AGENTS.md, skills, and managed-skills deployed to ~/.omp/agent/
  • macOS defaults — dock, finder, keyboard speed, trackpad, screenshot location managed via system.defaults
  • Fonts — nerd-fonts Iosevka via nix
  • Cleanup — brew --cleanup auto-removes anything not in the managed Brewfile

Deploy #

Two systems, two commands:

# Mac (darwin system + home-manager user config):
sudo darwin-rebuild switch --flake .#Alexiss-Mac-Studio

# Homeserver (NixOS — build on the Mac, apply remotely; no repo on the box):
nixos-rebuild switch --flake .#xenolandscapes.com \
  --target-host alexispurslane@192.168.1.138 --use-remote-sudo

Structure #

flake.nix                 — nix flake entrypoint; wires nixpkgs + home-manager + nix-darwin + NixOS together
                          (also declares the homeserver's disko layout and server-side inputs)
darwin.nix                — system-level config: homebrew taps/casks, macOS defaults, fonts
home.nix                  — user-level config: packages, shell, dotfiles, programs, herdr plugins, activation scripts
apps.nix                  — defines macOS login items and brew cask lists, imported by darwin.nix
overlays/
  bun.nix                 — custom nixpkgs overlay that fetches bun from GitHub releases
dotfiles/
  herdr/
    config.toml           — herdr keybindings, sidebar layout, plugin config
    plugins/
      sysmon.nix          — nix derivation for getpipher/herdr-sysmon (shell scripts, no build)
      jj-workspace.nix    — nix derivation for NathanFlurry/herdr-plugin-jj-workspace (cargo build)
  ghostty/config.ghostty  — ghostty terminal emulator settings
  zed/                    — zed editor settings, keymap, tasks
  omp/config.yml           — oh-my-pi agent configuration
  omp-skills/              — reusable agent skills (basecamp, ffmpeg, jujutsu, …)
  starship.toml            — cross-shell prompt theme
  inputrc                  — readline keybindings
  atuin.toml               — shell history search config
  jjui.toml                — jj TUI config
  hammerspoon.lua          — macOS window management
  karabiner.json           — keyboard remapping
  linearmouse.json         — mouse/trackpad acceleration config
homeserver/
  nixos/                   — the NixOS system modules (see below)
  containers/              — per-container quadlet definitions consumed by nixos/containers.nix
  secrets/                 — age-encrypted secrets.yaml + sops config (committed, encrypted)
  substack-mirror/         — mirror.py source (the bsky mirror app)
cleanup.sh                — removes stale brew casks, nix generations, and cache
cleanup-plists.sh          — removes stale launchd plists from macOS
README.md                 — this file
flake.lock                — pinned nix inputs

Key files #

flake.nix — The flake entrypoint. Declares nixpkgs (unstable), home-manager, and nix-darwin as inputs, sets the system to aarch64-darwin, and wires everything together. Home manager runs as a nix-darwin module so both system and user config are applied in a single darwin-rebuild switch.

darwin.nix — System-level macOS configuration. Manages homebrew taps, formulae, and casks declaratively; sets macOS defaults (dock, finder, keyboard, trackpad, screenshots); installs nerd fonts; and defines launchd services. The user account's home directory is declared here so home-manager can find it.

home.nix — The largest file; defines everything user-facing. Installs CLI packages via nix (bash, ripgrep, tmux, herdr, jjui, …), configures every managed program (starship, atuin, fzf, zoxide, eza, bat, git, ssh, delta, direnv), sets shell aliases and env vars, manages dotfile symlinks via xdg.configFile and home.file, and includes activation scripts for login items and herdr plugin symlinks.

apps.nix — A data file listing macOS login items (apps that launch at login) and brew casks, imported by both darwin.nix and the login-item activation script in home.nix. Kept separate so it can be shared without duplication.

NixOS — the homeserver #

The homeserver is a NixOS system (hostname xenolandscapes.com) built and deployed from this flake. The box is a pure consumer: it has no repo, no evaluation — every switch builds on the Mac and applies remotely. The data lives on bulk (media, /var/mnt/bulk) and a dedicated data partition (/var/data); the container-facing ownership is repaired at every boot via tmpfiles (the staged/restored data never carries the right uids).

homeserver/nixos/configuration.nix — The platform module: hostname (FQDN via hostName + domain), the operator user + git user (uid 1001:1002, knot's data owner), passwordless sudo, sshd with knot's git integration (AuthorizedKeysCommand, host keys reused from /var/data/knot/keys), the static LAN IP + tailscale, the firewall (exactly the public exposure: 22 25 80 443 465 993 22000 tcp + 22000/21027/41641 udp), systemd-boot, and the tmpfiles data-ownership repair.

homeserver/nixos/disko.nix — The sda partition layout (ESP → /boot, 150 G root → /, data → /var/data, labels via mkfs extraArgs). Single source: feeds both the nixos-anywhere install (diskoConfigurations) and the running system's fileSystems via the disko NixOS module.

homeserver/nixos/filesystems.nix — The mounts disko does not own: the bulk LVM volume (xfs, by-uuid), the /var/www bind into bulk, and lvm2's udev autoactivation.

homeserver/nixos/containers.nix — The six remaining containers (audiobookshelf, jellyfin, mox, syncthing, tranquil-pds-db/pds) via the quadlet-nix NixOS module. Consumes the shared per-container definitions in homeserver/containers/ (digest-pinned images); only the two secret-file paths are overridden to /run/secrets, and the db/pds units order after sops-nix. knot is deliberately absent — it runs natively (services.nix).

homeserver/nixos/nginx.nix — The ten vhosts (static site, jellyfin ×2, audiobookshelf, syncthing UI, knot server with websockets, the PDS, mox webmail ×2) + ACME (four certs, SANs via serverAliases, no-email registration).

homeserver/nixos/services.nix — The native services: knot (the tangled-core binary as the git user, container env preserved, knot server on 5555) and substack-mirror (a python3.withPackages env + the oneshot + 10-min timer; config.yaml from sops).

homeserver/nixos/secrets.nix — sops-nix: three whole-file secrets decrypted at activation (substack-config.yaml, the full tranquil-pds config.toml, the db .env) from homeserver/secrets/secrets.yaml, keyed by the age key at /var/lib/sops-nix/age/keys.txt.

homeserver/containers/*.nix — The per-stack quadlet definitions (image digests, volumes, ports, healthchecks) in the quadlet-nix shape, consumed verbatim by nixos/containers.nix.

homeserver/secrets/ — secrets.yaml (age-encrypted, committed — diffs are readable in cleartext), .sops.yaml (the creation rules / age recipient), and a README describing the bootstrap.

homeserver/substack-mirror/ — mirror.py (Substack RSS + Notes → Bluesky) and wipe.py; the runtime env is declared in services.nix.

plugins/sysmon.nix — A simple nix derivation that passes through a fetchFromGitHub source tree. No compilation needed — the plugin is all shell scripts. The output is a directory containing herdr-plugin.toml, scripts/poll.sh, scripts/metrics.sh, and the sidebar config snippet.

plugins/jj-workspace.nix — A buildRustPackage derivation that fetches the source, cargo-builds the Rust binary, and installs the full plugin tree (herdr-plugin.toml + target/release/jj-workspace + src/) to match what herdr's plugin loader expects.

Not a template #

This config contains hardcoded paths (/Users/alexispurslane), my SSH hosts, my git identity, my agent models, my specific brew casks, and my aesthetic preferences. If you want to do something similar, read the Home Manager manual and the nix-darwin manual — they're excellent.

What's still manual #

  • SSH keys and pi/omp auth tokens (secrets, not in the repo)
  • macOS text input sources and wallpaper (nix can't declaratively manage these)