Personal dotfiles managed with Nix Home Manager.
dot-config/
├── flake.nix # Entry point — defines all profiles
├── flake.lock # Pinned package versions (commit this!)
├── lib/
│ └── mkProfile.nix # Shared wrapper for all profiles
├── config/ # App configs (symlinked into ~/.config/)
│ ├── nvim/ # LazyVim
│ ├── ghostty/
│ ├── tmux/
│ └── wezterm/
├── templates/ # Placeholder files seeded into $HOME on fresh machines
│ ├── env.local.example # → ~/.env.local
│ └── ssh-config.local.example # → ~/.ssh/config.local
├── profiles/
│ ├── personal.nix # core + shell + git + fnm + php + nginx + extras + ssh
│ ├── work.nix # core + shell + git + fnm + nginx + devops + ssh
│ └── minimal.nix # core + shell + git only
└── modules/
├── core.nix # Always-on: tmux, lazygit, bat, eza, fzf, ripgrep, zoxide, starship, direnv
├── nvim.nix # Neovim (unstable) + LazyVim config symlink
├── configs.nix # Symlinks ghostty, tmux, wezterm into ~/.config/
├── shell.nix # zsh + oh-my-zsh + aliases + hms switcher + ~/.env.local seeding
├── git.nix # git config + 1Password SSH signing
├── fnm.nix # fnm (Node version manager) + bun + yarn
├── php.nix # PHP 8.5 + Composer + Laravel installer via php.new (auto-bootstraps)
├── ssh.nix # ssh config: 1Password agent, keep-alives, ~/.ssh/config.local include
├── nginx.nix # mkcert + nginx-add/nginx-remove helpers for local server configs
├── devops.nix # kubectl, helm, gcloud, mkcert, sops, k9s
└── extras.nix # ollama, lazydocker, lazysql, cloudflared
# 1. Install Nix
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sudo sh -s -- install
# If that fails on macOS, use the .pkg installer: https://dtr.mn/determinate-nix
# 2. Restart shell
exec zsh
# 3. Clone dotfiles
git clone <your-repo-url> ~/dot-config
# 4. Back up any files Home Manager will own
mv ~/.zshrc ~/.zshrc.bak 2>/dev/null
mv ~/.zshenv ~/.zshenv.bak 2>/dev/null
mv ~/.gitconfig ~/.gitconfig.bak 2>/dev/null
mv ~/.config/nvim ~/.config/nvim.bak 2>/dev/null
rm ~/.config/ghostty ~/.config/tmux ~/.config/wezterm 2>/dev/null # if they're broken symlinks
# 5. Stage files for Nix (flakes require git tracking)
git -C ~/dot-config add .
# 6. Activate your profile
cd ~/dot-config && nix run github:nix-community/home-manager/release-24.11 -- switch --flake .#personal
# 7. Reload shell — hms function is now available
exec zshThe first switch also bootstraps things Nix doesn't manage:
- PHP, Composer, Laravel installer — downloaded automatically via php.new
into
~/.config/herd-lite/bin(seemodules/php.nix) ~/.env.localand~/.ssh/config.local— seeded fromtemplates/as placeholder skeletons; existing files are never overwritten
Then finish up manually:
# Fill in machine-local secrets (from 1Password / previous machine)
nvim ~/.env.local # API tokens, exported env vars
nvim ~/.ssh/config.local # private SSH hosts (IPs, users)
# Install a Node version (fnm itself comes from Nix, versions don't)
fnm install --lts && fnm default lts-latesthms personal # core + shell + git + node + php + nginx + extras + ssh
hms work # core + shell + git + node + nginx + devops (kubectl, helm...) + ssh
hms minimal # core + shell + git onlyAfter the first nix run ... bootstrap, hms is always available as a shell function.
1. Find the package name
Search at https://search.nixos.org/packages or:
nix search nixpkgs <name>2. Add it to the right module
For something you always want:
# modules/core.nix
home.packages = with pkgs; [
tmux
your-new-package # add here
...
];For something only in one profile, add it directly in the profile file:
# profiles/personal.nix
home.packages = with pkgs; [ some-personal-tool ];For a package that needs the latest version from unstable:
# any module
home.packages = [ pkgs-unstable.some-package ];3. Apply
hms personal # or whichever profile you're onAll zsh config lives in modules/shell.nix. You don't edit ~/.zshrc directly — Home Manager generates it.
# modules/shell.nix — programs.zsh.shellAliases
shellAliases = {
k = "kubectl";
cat = "bat";
# add yours here
};# modules/shell.nix — programs.zsh.initExtra
initExtra = ''
# your existing init content...
function myfunction() {
echo "hello $1"
}
'';# modules/shell.nix or the relevant profile
home.sessionVariables = {
MY_VAR = "value";
};home.sessionPath = [
"$HOME/my/custom/bin"
];Override or extend in a profile instead of the shared module:
# profiles/work.nix
programs.zsh.shellAliases = {
kprod = "kubectl --context=production";
};
home.sessionVariables = {
WORK_ENV = "kitabisa";
};After any change, apply with:
hms personal # or your current profile1. Create the config directory
mkdir -p ~/dot-config/config/myapp
# add your config files inside it2. Register it in modules/configs.nix
xdg.configFile."myapp".source =
config.lib.file.mkOutOfStoreSymlink "${dotfiles}/myapp";3. Apply
hms personalHome Manager will symlink ~/dot-config/config/myapp → ~/.config/myapp.
1. Create profiles/myprofile.nix
{ config, pkgs, lib, ... }:
{
imports = [
../modules/core.nix
../modules/nvim.nix
../modules/configs.nix
../modules/shell.nix
../modules/git.nix
# add or remove modules as needed
];
programs.git.userName = "Fiqry Choerudin";
programs.git.userEmail = "your@email.com";
}2. Register it in flake.nix
homeConfigurations = {
personal = mkProfile ./profiles/personal.nix;
work = mkProfile ./profiles/work.nix;
minimal = mkProfile ./profiles/minimal.nix;
myprofile = mkProfile ./profiles/myprofile.nix; # add this
};3. Add it to the hms completions in modules/shell.nix
local valid_profiles=(personal work minimal myprofile)4. Apply
git -C ~/dot-config add .
hms myprofileNode.js is managed by fnm (Fast Node Manager). It auto-switches versions based on .nvmrc or .node-version files when you cd into a project.
fnm install --lts # latest LTS
fnm install 22 # specific major
fnm install 22.14.0 # exact versionfnm default lts-latestecho "22" > .nvmrc # or .node-versionfnm will auto-switch when you enter the directory.
pnpm is installed globally per Node version via npm:
npm install -g pnpm@10PHP is not installed from nixpkgs — modules/php.nix bootstraps static
binaries from php.new (Herd Lite) into
~/.config/herd-lite/bin: php 8.5, composer, and the laravel installer.
- A home-manager activation hook installs them automatically if missing, so a
fresh machine gets PHP from the first
hmsrun — no manual step. - PATH and
PHP_INI_SCAN_DIRare managed by the module; the binaries themselves are plain files, not Nix-managed. - To update PHP, re-run the installer (bump the version in
php.nixfirst if needed):/bin/bash -c "$(curl -fsSL https://php.new/install/mac/8.5)" - To remove:
~/.config/herd-lite/bin/uninstall_herd_liteand drop the module import.
cd ~/dot-config
nix flake update # bumps all inputs to latest
hms personal # rebuild with new versions
git add flake.lock && git commit -m "chore: update flake inputs"To update only one input (e.g. neovim without touching nixpkgs):
nix flake lock --update-input nixpkgs-unstableAdd to home.sessionVariables in the relevant module or profile:
# modules/shell.nix or profiles/work.nix
home.sessionVariables = {
GOPRIVATE = "github.com/your-org";
KUBECONFIG = "$HOME/.kube/config";
};direnv is included in core.nix. Create a .envrc in any project folder:
# ~/projects/myapp/.envrc
export DATABASE_URL=postgres://localhost/myapp
export API_URL=http://localhost:3000Then allow it once:
cd ~/projects/myapp
direnv allowVars load automatically when you cd in and unload when you leave. Add .envrc to your global gitignore or commit non-sensitive ones.
Already wired up in shell.nix. This file is machine-local and never
committed (on fresh machines it's seeded from templates/env.local.example):
# ~/.env.local
export GITHUB_TOKEN=ghp_xxx
export OPENAI_API_KEY=sk-xxxNever put a real secret inside a
.nixfile — everything in the Nix config is copied to the world-readable/nix/store.
modules/ssh.nix keeps generic SSH settings (1Password agent, keep-alives) in
the repo and Includes ~/.ssh/config.local for anything machine-private —
server IPs, usernames, per-host keys. Like .env.local, it's seeded from
templates/ssh-config.local.example and never committed:
# ~/.ssh/config.local
Host myserver
HostName 203.0.113.10
User ubuntu
Pull secrets live instead of storing them in plaintext:
# ~/.env.local
export GITHUB_TOKEN=$(op read "op://Personal/GitHub Token/credential")Or prefix any command to inject secrets without writing them to disk:
op run --env-file=.env -- npm run devThese are NOT managed by Nix — install them manually:
| Tool | Reason |
|---|---|
| aerospace, yabai, skhd | macOS window managers — not in nixpkgs |
| ghostty, wezterm | Configs managed here; binaries via Homebrew cask |
| caddy, redis, nginx | brew services gives launchd integration |
| rustup | Manages its own toolchain |
| nvm | No longer used — replaced by fnm (managed by Nix) |
| orbstack | macOS kernel extension |