This repo is a Nix flake that manages most of my setup on macOS and fully manages machines I have that run NixOS as their operating system. It also contains as much configruation as I can make work on other Linux distros such as Ubuntu.
flake.nixdefines inputs, outputs, and instantiates host configurations vialib/functionslib/contains helper functions:mkNixosHost- constructs NixOS system configurationsmkDarwinHost- constructs nix-darwin system configurationsmkHomeConfig- constructs Home Manager configurationsgenebeanLib- shared platform-detection helpers passed to all modules viaextraSpecialArgs
modules/contains Nix modules organized by type:modules/genebean/- reusable, option-driven modules under thegenebean.*namespacemodules/genebean/home/- Home Manager modules; exposed ashomeManagerModules.genebeanmodules/genebean/darwin/- nix-darwin companion modules; exposed asdarwinModules.genebean
modules/shared/- shared modules imported by multiple hostsmodules/shared/home/general/- Home Manager config for all GUI usersmodules/shared/home/linux/- Home Manager config for Linux-specific appsmodules/shared/nixos/- NixOS modules (i18n, flatpaks, restic, etc.)
modules/hosts/- host-specific configurationsmodules/hosts/nixos/- NixOS host configs and hardware configsmodules/hosts/darwin/- macOS host configsmodules/hosts/home-manager-only/- Home Manager-only configs
This repo uses the following tools for code quality:
- nixfmt - Formats Nix files. Run
nix fmt .to format all files. - deadnix - Finds unused code in Nix files.
- statix - Checks Nix code for common issues and style problems.
Pre-commit hooks are configured in .pre-commit-config.yaml and run automatically before commits.
Run pre-commit install after checkout to make sure it gets used.
CI validation is defined in .github/workflows/validate.yml and mirrors what is done by pre-commit.
This repo historically contained my dot files. Historically symlinked files on Windows are still in windows/. Everything else is just in git history now.
Sometimes hosts, or their storage, need replacing... sepcially ones that run on SD cards like kiosk-gene-desk. When that time comes, here is how to get it back up and running.
These hosts use disko for partitioning, impermanence for a tmpfs root
(wiped every boot, with an explicit persist allowlist), and
nixos-anywhere for install - not a flashed system image. disko wipes
the target disk from scratch every time, so this is the same process for
a first-time install and a full replacement (dead SD card, etc).
-
Build the installer image (on mightymac, or anywhere with the flake):
nix build .#rpi4-installer. This is a genericnixos-raspberrypiinstaller image, pre-decompressed so Raspberry Pi Imager can flash it directly - it silently corrupts the card if you hand it the raw.img.zstinstead, since Imager doesn't decompress zstd itself.resultis the flashable.imgfile. -
Flash it to a USB drive, not the SD card - Raspberry Pi Imager, "Use custom image", pick
result. -
Connect a display and boot the Pi from that USB drive with the SD card removed. Both matter:
nixos-anywhere/diskocan't repartition a disk the system is currently running from, and a Pi's boot order can prefer the SD card over USB if one's already inserted - booting from USB with no SD card present avoids that ambiguity entirely. A display is required too: the installer sets a random root password on boot and prints it on screen along with its IP - read both off the display. You'll need the password again fornixos-anywherein step 5. Once it's up, insert the SD card - it's just a second, currently-empty disk to the running installer at this point, safe to insert at any time after boot. -
Seed the bootstrap files.
diskowipes/persistfrom scratch, so the sops age key has to exist on the target beforenixos-install's activation runs, orsops-install-secretsfails on first boot with no way to recover short of a manual SSH patch. From the repo root:scripts/prep-install-bootstrap.sh <hostname>
This derives the age key from a per-host SSH key already in
modules/shared/secrets.yaml, seeds a clock file so the fresh boot's NTP sync starts from a real floor instead of the image build's fallback date (no RTC on these Pis), and tags the last 5 restic snapshots already in the backup repo for that hostname aspre-reinstall, so they survive the fresh install's own backup rotation - this talks to the repo directly (mightymac is already a valid recipient for the restic secrets), so it works regardless of whether the host being replaced is still reachable. Confirm the derived age recipient printed at the end matches the host's entry in.sops.yamlbefore continuing. -
Install:
nixos-anywhere --flake ~/repos/dots#<hostname> \ --extra-files ./nixos-anywhere-extras-<hostname> root@<installer-ip>
It'll prompt for the root password from step 3 to make its initial SSH connection, then reboots into the real system when done.
-
Restore state. SSH in once it's back up, then:
sudo kiosk-restic-full-restore # lists available snapshots sudo kiosk-restic-full-restore <id> # restores from a specific one
Use the specific
pre-reinstall-tagged snapshot ID from step 4, not whatever's newest - a boot-triggered catch-up backup on the fresh install can capture empty state and become "latest" before you get a chance to restore, so the tool never guesses for you. This restores Home Assistant'shass-browser_modchromium registration, atuin's sync-server login, and the tailscale node identity, and stops/starts the affected services itself. -
Verify: chromium shows the kiosk page (browser_mod registered in Home Assistant),
atuin statusshows a recent sync,tailscale statusshows the expected node name (if it comes up suffixed like<hostname>-1, the old pre-replacement node is probably still listed as active in the Tailscale admin console under the plain name - remove it there).
The directions below are all a bit dated and likely incomplete 😔 They will be updated as time make practical.
- run
xcode-select --installto install the command-line developer tools (this includes the Apple's stock version of Git). - create ed25519 ssh key via
ssh-keygen -t ed25519 - add key to GitHub account
- run macOS graphical installer from https://determinate.systems/posts/graphical-nix-installer
- run
mkdir ~/repos - run
cd ~/repos - run
git clone git@github.com/genebean/dots - create keys for SOPS via
mkdir -p ~/Library/Application\ Support/sops/age && nix run nixpkgs#ssh-to-age -- -private-key -i ~/.ssh/id_ed25519 > ~/Library/Application\ Support/sops/age/keys.txt && nix run nixpkgs#ssh-to-age -- -i ~/.ssh/id_ed25519.pub >~/Library/Application\ Support/sops/age/pub-keys.txt - run
cat ~/Library/Application\ Support/sops/age/pub-keys.txt |pbcopy - edit
.sops.yamland:- paste copied data into a new line under keys
- add creation rule
- add to common rule
- run
mkdir modules/home-manager/hosts/$(hostname -s) - run
nix run nixpkgs#sops -- modules/home-manager/hosts/$(hostname -s)/secrets.yaml - Add entries for
local_private_envcontaining anything you want exported as env vars or local aliases that you want to keep privatetailscale_key
- create
modules/home-manager/hosts/darwin/$(hostname -s)/<username>.nixbased on needs for this machine - run
mkdir modules/hosts/darwin/$(hostname -s) - create
modules/hosts/darwin/$(hostname -s)/default.nixbased on need for this machine - add entry to
flake.nix - if not a fresh install of macOS,
- run
brew leavesand look for things installed from taps you don't want any more - uninstall the program and the tap if not adding it to nix
- run
- run
git add . - run
git status- it should look something like this:gene.liverman@mightymac dots % git status On branch main Your branch is up to date with 'origin/main'. Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: .sops.yaml modified: flake.nix new file: modules/home-manager/hosts/mightymac/gene.liverman.nix new file: modules/home-manager/hosts/mightymac/secrets.yaml new file: modules/hosts/darwin/mightymac/default.nix
- run
sudo mv /etc/nix/nix.conf{,.before-nix-darwin} - run
sudo mv /etc/zshenv{,.before-nix-darwin} - run
nix run --extra-experimental-features 'nix-command flakes repl-flake' nix-darwin -- check --flake ~/repos/dots - Run
nix run --extra-experimental-features 'nix-command flakes repl-flake' nix-darwin -- switch --flake ~/repos/dots- if prompted, run
sudo mv /etc/shells{,.before-nix-darwin} - if prompted, run
sudo mv /etc/zshenv{,.before-nix-darwin} - if prompted, you may also have to move or remove
~/.zshrc - on the first (or several) run(s) homebrew may well fail due to previously installed casks or programs in
/Applications. You may have to runbrew install --force <package name>to fix this - you may have to run brew multiple times to fix things
- if prompted, run
- in Settings > Privacy & Security > App Management you will need to allow iTerm
- After the nix command finally works, open a new iTerm window and it should have all the nixified settings in it.
- Go into iTerm2's preferences and use the Hack Nerd Mono font so that the prompt and other things look right. You will likely also want to adjust the size of the font.
- run
sudo cp /etc/pam.d/sudo_local{.template,}- this will generate a popup asking permission - run
sudo nvim /etc/pam.d/sudo_localand uncomment line as directed by top comments - save via
!wwhich will generate a popup asking permission
Nix installs and configures Atuin, but you still need to log into the server:
- run
atuin import autoto import the shell history from before Atuin was installed and running - run
read -s akeyand enter the encryption key - run
read -s apassand enter the user password - run
atuin login --key=$akey --password=$apass --username=gene
- clone this repo
- create keys for SOPS via
mkdir -p ~/.config/sops/age && nix run nixpkgs#ssh-to-age -- -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt && nix run nixpkgs#ssh-to-age -- -i ~/.ssh/id_ed25519.pub > ~/.config/sops/age/pub-keys.txt - copy output of
~/.config/sops/age/pub-keys.txt - add entries to
.sops.yaml - run
sops modules/hosts/nixos/$(hostname)/secrets.yaml- if there is an empty yaml file in where you target you will get an error... just delete it and try again
- edit
sops modules/hosts/nixos/$(hostname)/default.nixand add the Tailscale service and the block of config for sops.- if there is an empty yaml file in where you target you will need to delete it