As a senior solutions architect, I work across multiple projects and environments. Having a reproducible development setup is crucial for maintaining consistency and quickly spinning up new environments. Here’s how I’ve structured my Nix configuration to achieve this on macOS.
My configuration uses a combination of:
~/.nix-config/
├── flake.nix # Entry point and dependencies
├── home/ # Home-manager configurations
│ ├── default.nix # Main home configuration
│ ├── core.nix # Core packages and settings
│ ├── neovim.nix # Neovim setup
│ ├── zsh.nix # Shell configuration
│ ├── git.nix # Git configuration
│ ├── tmux.nix # Terminal multiplexer
│ ├── kitty.nix # Terminal configuration
│ └── aerospace.nix # Window manager
├── modules/ # System modules
│ └── homebrew.nix # Homebrew integration
├── systems/ # System-specific configs
│ └── darwin.nix # macOS system settings
└── shared/ # Shared variables
└── variables.nix # User and system variables
The flake.nix serves as the entry point, defining inputs and outputs:
{
description = "Jeanre's system configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
darwin = {
url = "github:lnl7/nix-darwin/master";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, darwin, home-manager}:
let
vars = import ./shared/variables.nix;
in
{
darwinConfigurations = {
"Jeanres-MacBook-Pro" = darwin.lib.darwinSystem {
specialArgs = { inherit vars; };
system = "aarch64-darwin";
modules = [
./systems/darwin.nix
./modules/homebrew.nix
home-manager.darwinModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.extraSpecialArgs = { inherit vars; };
home-manager.users."${vars.username}" = import ./home;
}
];
};
};
};
}
My core development environment includes essential CLI tools:
# home/core.nix
{
home.shellAliases = {
cd = "z";
};
programs.ripgrep.enable = true;
programs.fd.enable = true;
programs.jq.enable = true;
programs.eza.enable = true;
programs.htop.enable = true;
programs.fzf.enable = true;
programs.zoxide = {
enable = true;
enableZshIntegration = true;
};
programs.tealdeer.enable = true;
}
This gives me modern replacements for traditional Unix tools:
ripgrep for fast searchingfd for finding fileseza for better ls outputzoxide for smart directory navigationfzf for fuzzy findingI use Zsh with several enhancements:
# home/zsh.nix
{...}: {
programs.zsh = {
enable = true;
enableCompletion = true;
defaultKeymap = "viins";
shellAliases = {
dr = "sudo darwin-rebuild switch --flake .#Jeanres-MacBook-Pro";
cat = "bat";
};
};
programs.carapace = {
enable = true;
enableZshIntegration = true;
};
programs.starship = {
enable = true;
enableZshIntegration = true;
};
}
Key features:
dr)My Neovim setup uses the latest Lua-based configuration with lazy.nvim:
# home/neovim.nix
{ pkgs, ... }:
{
programs.neovim = {
enable = true;
defaultEditor = true;
vimAlias = true;
package = pkgs.neovim-unwrapped;
};
xdg.configFile = {
"nvim/init.lua" = {
source = ./neovim/init.lua;
};
"nvim/lua" = {
source = ./neovim/lua;
recursive = true;
};
"nvim/lsp" = {
source = ./neovim/lsp;
recursive = true;
};
};
}
The Lua configuration includes:
My Git setup is optimized for modern workflows:
# home/git.nix
{ vars, ... }:
{
programs.git = {
enable = true;
settings = {
user = {
email = vars.email;
name = vars.name;
};
alias = {
co = "checkout";
ci = "commit";
di = "diff";
dc = "diff --cached";
fa = "fetch --all";
pf = "push --force-with-lease";
};
init = {
defaultBranch = "main";
};
pull = {
rebase = true;
};
rebase = {
autoSquash = true;
autoStash = true;
};
};
};
}
My Tmux configuration is feature-rich with plugins:
# home/tmux.nix
{ pkgs, ... }:
{
programs.tmux = {
enable = true;
baseIndex = 1;
plugins = with pkgs; [
tmuxPlugins.vim-tmux-navigator
tmuxPlugins.resurrect
tmuxPlugins.continuum
tmuxPlugins.catppuccin
tmuxPlugins.battery
tmuxPlugins.online-status
];
};
}
Key features:
For GUI applications, I use Homebrew integration:
# modules/homebrew.nix
{ ... }:
{
homebrew = {
enable = true;
onActivation = {
autoUpdate = true;
upgrade = true;
cleanup = "zap";
};
taps = [
"nikitabobko/tap"
"vladdoster/formulae"
];
brews = [
"reattach-to-user-namespace"
"opencode"
];
casks = [
"kitty"
"docker-desktop"
"aerospace"
"font-jetbrains-mono-nerd-font"
"slack"
"microsoft-teams"
"utm"
"discord"
];
};
}
The macOS system settings are managed through systems/darwin.nix:
# systems/darwin.nix
{ pkgs, vars, ... }:
{
nix = {
package = pkgs.nixVersions.latest;
extraOptions = ''
extra-platforms = x86_64-darwin
experimental-features = nix-command flakes
'';
gc = {
automatic = true;
interval = { Weekday = 0; Hour = 0; Minute = 0; };
options = "--delete-older-than 30d";
};
};
security.pam.services.sudo_local.touchIdAuth = true;
services.openssh.enable = true;
system = {
defaults = {
screencapture = {
location = "~/Documents/Screenshots";
};
dock = {
mru-spaces = false;
autohide = true;
};
};
keyboard = {
enableKeyMapping = true;
remapCapsLockToEscape = true;
};
};
}
~/.nix-config/dr (alias for darwin-rebuild switch --flake .).nix file in home/modules/homebrew.nixThis Nix configuration provides a robust, reproducible development environment that scales across different projects and machines. The modular structure makes it easy to maintain and extend, while the declarative approach ensures consistency and reduces manual configuration errors.
The combination of modern CLI tools, a powerful Neovim setup, and comprehensive system management creates an efficient development environment that adapts to my needs.
Have questions about my Nix setup or want to share your own configuration patterns? Feel free to reach out or open an issue on the nix-config repository.
| Contact: jeanre.swanepoel@gmail.com | +27 68 618 3487 |