This repo primarily consists as an exercise in using a nix flake to manage our system and user configurations via
- nixpkgs
- home-manager
- standard symlink farming
- (optionally) devbox.
We hope it serves as a personal motivation to investigate NixOS more generally. For this reason we purposefully avoid some of the more layered frameworks (and their utility libraries) mentioned in the References below, but try to take some structural cues.
- ./host: Contains system specific configurations targetting hardware architectures. In theory these configurations are at the highest level, but we suspect that architecture-specific configurations leak into lower levels, e.g., with new Apple Silicon based macs. Cf. Mitchell Hashimoto's nixos setup to see how vms can be used to abstract some of these nuances away. We may settle upon a pattern where some hosts are abstract, to have common files to apply to a company mandated MacBook vs a personal one.
- home-manager configurations.
- Configuration files symlinked by home-manager. Produces links to read-only files so not ammenable to rapid iteration.
- lib: Common boilerplate library functions used in this flake, e.g., for building nix-darwin and standalone home-manager configurations.
- overlays containing package overrides, e.g., to use a different channel.
- ./etc: Configurations not managed by home-manager, but via a symlink farm manager, e.g., our simple shell script stash
git clone --recurse-submodules https://github.com/shawnohare/nixos-config
cd ~/nixos-configExcept on NixOS, we must install nix (the package manager) itself to build the
flake. Consider also determinate. NOTE: As of 2026 the
Determinate Systems installer no longer installs vanilla nix, but their own
variant.
Note that the Determinate Systems installer will not necessarily result in an identical setup as the official installer. In particular, some default channels are not necessarily set. This can make uninstalling nix-darwin a pain. Confer the nix-installer repo for nuances.
Determinate systems nix is now directly compatible with nix-darwin 24.11. Both detsys nix and nix-darwin want to manage nix directly.
- The Determinate Systems graphical installer.
- The Determinate Systems cli installer
if [ "$(uname -s)" = "Darwin" ]; then xcode-select --install fi curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
- The official installer
if [ "$(uname -s)" = "Darwin" ]; then xcode-select --install fi sh <(curl -L https://nixos.org/nix/install) --daemon
macOS systems can managed with a combination of nix-darwin and home-manager. nix-darwin surfaces a NixOS like system configuration at the system level. The basic options are
- Build nix-darwin and include home-manager as a nix-darwin module. In theory this option is preferred, but we have run into some stability issues.
- Build a stand-alone home-manager.
Confer Nix Academy's nix on macOS.
We install nix-darwin by building this flake for a specific host (e.g., "work")
corresponds to one the darwinConfigurations attributes in
flake.nix.
Clone and build the flake for a specified target, e.g.,
cd nixos-config
target=work # defaults to $(hostname -s) if omitted
./switch "${target}"
# Or, roughly the equivalent
nix --extra-experimental-features "nix-command flakes" build ".#darwinConfigurations.${target}.system"
sudo result/sw/bin/darwin-rebuild switch --flake ".#${target}"After nix-darwin is built, darwin-rebuild is available. Rebuild the system
and user configuration via
./switch "${target}"
# or
sudo darwin-rebuild switch --flake "#${target}"After initially installing nix or macOS updates nix-darwin switch can fail
for a variety of reasons.
nix-darwin might fail if it cannot find /run. Try
printf 'run\tprivate/var/run\n' | sudo tee -a /etc/synthetic.conf
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -tnix-darwin might fail to write certain files if they already exist. Try
./switch backup
# or
sudo mv /etc/nix/nix.conf /etc/nix/nix.conf.before-nix-darwin
sudo mv /etc/shells /etc/shells.before-nix-darwin
sudo mv /etc/bashrc /etc/bashrc.before-nix-darwin
sudo mv /etc/zshrc /etc/zshrc.before-nix-darwin
sudo mv /etc/zshenv /etc/zshenv.before-nix-darwinFor cert issues cf https://discourse.nixos.org/t/ssl-ca-cert-error-on-macos/31171/6
home-manager deals with user-level configuration. For macOS and non-NixOS Linux
systems it is possible to install home-manager in standalone mode. To do this,
simply omit the system building steps outlined above.
# Initially, to install home-manager itself
export NIXPKGS_ALLOW_UNFREE=true
nix run home-manager --verbose --show-trace -- switch --impure --flake ".#${target}"
# Once home-manager is installed
home-manager switch --impure --flake ".${target}"
# or in either case, use the helper script
./switch home ${target}The primary management options are
- NixOS: include home-manager as a module.
- macOS: Use nix-darwin on macOS and include home-manager as a module. This is generally our preferred method on macOS, though it does mean switching user configurations entails a system switch and sudo password.
- Linux / macOS: Use home-manager in a standalone fashion, but configured within the flake. This is the only option for non-NixOS Linux distributions.
The tools we use generally fall into the following categories.
- Common, relatively static utilities we rarely update. These can be effectively managed by a stable release of nixpkgs, or the system package manager.
- Applications and toolchains we wish to update frequently (e.g., neovim).
- Applications not available to nixpkgs or more easily installed manually, e.g., many macOS GUI applications.
When using a stable version of nixpkgs, there does not seem to be an elegant
way of frequently updating individual packages.
In our setup we prefer to follow nixpkgs-unstable and install most packages
with either home-manager or devbox global.
It is a good idea to periodically update the flake's lockfile.
To update a specific flake input (e.g., nixpkgs in the example)
nix flake update nixpkgs
# previously this was
nix flake lock --update-input nixpkgs --update-input <package>`Originally we managed configuration files via committing them to a version
controlled repository and then symlinking them into the appropriate location
via tools such as GNU stow. The benefit here is that once the files are
linked, changes can be made to the source and they will propagate without
re-linking.
Then we utilized a bare git repository that contained all configuration files.
The benefit here is that there are no symlinks at all, so changes are immediate
and require no rebuilds. The downside with a bare repo is that one must
explicitly pass the git dir and work tree as arguments to git, which itself
is more or less trivial. But then the basic commands tend to have a lot of
clutter. Many things get ignored, there are many untracked files, etc.
Using home-manager's builtin modules to configure applications or even the symlinking capabilities does involve a rebuild after any configuration change. This is because the source file in the repository gets copied to the nix store and then linked. For rapid configuration changes this is a bit of a pain.
Some applications we like to use while on macOS that are generally not managed by this repo are
- 1password
- Raycast (spotlight replacement)
- raindrop.io (bookmark manager)
- obsidian.md (notes)
- Orion & firefox (web browser)
Some example nixos-config config repositories we take inspiration from.
- This flake was initially a near clone of Matthias' NixOS & macOS configuration flake
- Mitchell Hashimoto's nixos config
- Dustin Lyon's nixos-config
Other resources