This repository is home to the nix code that builds my systems.
Nix allows for easy to manage, collaborative, reproducible deployments. This means that once something is setup and configured once, it works forever. If someone else shares their configuration, anyone can make use of it.
This flake is configured with the use of several flake helpers. Have a look at the inputs for a full comprehensive list.
See docs/ARCHITECTURE.md for a detailed explanation of how the repository is organized.
This configuration uses Lix, a modern, community-driven
Nix implementation with flakes and nix-command experimental features enabled by
default. If you're using standard Nix, you'll need to enable these features
manually or add --extra-experimental-features "flakes nix-command" to commands.
| Action | Darwin (macOS) | NixOS (Linux) |
|---|---|---|
| Build | nix build '.#darwinConfigurations.<host>.system' --accept-flake-config |
nix build '.#nixosConfigurations.<host>.system' --accept-flake-config |
| Apply | darwin-rebuild switch --flake .#<host> |
nixos-rebuild switch --flake .#<host> |
| Build + switch | just build-switch <host> |
just build-switch <host> |
nix develop # enter devshell
nix fmt # format code (nixfmt via treefmt)
nix flake check # validate flake
just update # update flake inputs
just gc # garbage collectionSecrets are managed with agenix and a
git-age-filter that keeps
files encrypted at rest. Both the Linux and macOS bootstrap flows reference the
subsections below.
Some files in this repo are encrypted at rest with git-age-filter and check
out as ciphertext on a fresh clone. The filter is configured per-repo, so
install must run first. The git-age-filter and age tools come from the
devshell, so enter it first. To decrypt (one-time Yubikey touch):
nix develop --impure # enter devshell (provides git-age-filter, age)
git-age-filter install # configure the per-repo filter
age -d -i "$PRJ_ROOT/identities/age/yubikey-id-5f449e60.txt" "$PRJ_ROOT/.age/local-key.age" > "$PRJ_ROOT/.age/local-key"
chmod 600 "$PRJ_ROOT/.age/local-key"
git-age-filter unlock # decrypt the working treeSee the git-age-filter README for how the filter works and its day-to-day commands.
To provision a new YubiKey as an age identity and grant it access to both the git-age-filter local key and agenix secrets, see docs/adding-a-yubikey-age-key.md.
A new host needs its SSH public key added to the secrets config so agenix can rekey secrets for it.
-
Retrieve the host SSH public key:
# macOS only: generate the host key first if it doesn't exist sudo /usr/libexec/sshd-keygen-wrapper cat /etc/ssh/ssh_host_ed25519_key.pub
-
Add the host key to secrets configuration:
- Edit
secrets/secrets.nixand add the new host's public key - Rekey all secrets:
agenix --rekey
- Edit
The bootstrap process uses a special flag to disable agenix secrets during the initial install, since the host SSH keys don't exist yet.
WARNING: This will completely erase the target disk and create a new partition scheme using disko.
-
Boot the target machine into a NixOS installer ISO
-
Ensure SSH access to the target machine
-
Deploy with nixos-anywhere:
nix run github:nix-community/nixos-anywhere -- \ --flake '.#<hostname>' \ --override-input bootstrap github:boolean-option/true \ root@<target-ip>
Replace
<hostname>with your machine name (e.g., DeepThought) and<target-ip>with the target machine's IP address.The command will:
- Use disko to partition and format the disk according to
disk-config.nix - Install NixOS with the bootstrap flag enabled (secrets disabled)
- Deploy your system configuration
- override-input might not work
- Use disko to partition and format the disk according to
-
Restarting a failed nixos-anywhere run:
If
nixos-anywheredoes not complete and rerunning fails because the target disk is in a partial state, wipe the target disk from the installer shell and rerun the deployment:[root@nixos:~]# umount -R /mnt || true [root@nixos:~]# swapoff -a || true [root@nixos:~]# wipefs -a /dev/sda [root@nixos:~]# sgdisk --zap-all /dev/sda [root@nixos:~]# partprobe /dev/sda || true
Verify
/dev/sdais the intended target disk before running these commands.
For manual partitioning and install (kept for historical reference), see docs/manual-nixos-install.md.
-
Onboard the new host — add its SSH key and rekey secrets.
-
Switch to production configuration:
sudo nixos-rebuild switch --flake .#<hostname>This rebuild uses the default configuration (without the bootstrap flag), enabling all secrets.
Grant your terminal Full Disk Access first. The Lix/Nix installer needs to write to protected locations (e.g.
/nix,/etc); without it the install fails. Add your terminal (Terminal.app, iTerm, Ghostty, etc.) under System Settings → Privacy & Security → Full Disk Access, then restart the terminal.
-
Install Lix:
curl -sSf -L https://install.lix.systems/lix | sh -s -- install -
Install Homebrew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-
Clone this repository and enter the development shell:
git clone <repository-url> cd nixcfg nix develop --impure
The devshell provides agenix (with yubikey support), darwin-rebuild, and other necessary tools.
-
Decrypt repository secrets:
Follow Decrypt repository secrets (git-age-filter) to install the filter and unlock the working tree.
-
Onboard this host — add its SSH key and rekey secrets.
-
Add yourself as a trusted Nix user:
Edit
/etc/nix/nix.custom.confand settrusted-usersto match the value inflake.nix:# adjust according to flake.nix trusted-users = root chernand @admin
-
Build the Darwin configuration:
nix build ".#darwinConfigurations.$(hostname).system" --accept-flake-config -
Apply the configuration:
sudo ./result/sw/bin/darwin-rebuild switch --flake ".#$(hostname)"
After the initial setup, use the standard darwin-rebuild command:
darwin-rebuild switch --flake .#<hostname>