A modern, safe, repeatable local-development environment bootstrapper for macOS, Linux, WSL, and ephemeral development containers.
This repository is designed to be boring, explicit, and easy to maintain. It avoids hidden side effects, legacy plugin managers, destructive installers, and machine-specific assumptions.
- Zsh configuration with small, focused files
- Git defaults, aliases, global ignore rules, and conditional identity support
- Homebrew package management through
Brewfile - Runtime/tool version management through
mise - Optional Starship prompt configuration
- Optional direnv configuration
- A local
dotcommand for install, update, health checks, and maintenance - Neovim starter configuration using modern Lua-based setup
- Shell linting and formatting checks through GitHub Actions
- Safe by default — existing files are backed up before being replaced.
- Idempotent — scripts can be run repeatedly without breaking the environment.
- Cross-platform — supports macOS, Linux, WSL, and development containers where practical.
- No secrets in Git — tokens, credentials, and machine-specific values stay out of the repo.
- Small composable scripts — easy to inspect, debug, and replace.
- Modern tooling — Homebrew, mise, direnv, Starship, ShellCheck, shfmt, and Neovim.
.
├── bin/
│ └── dot # Main command wrapper
├── config/
│ ├── direnvrc # Optional direnv defaults
│ └── starship.toml # Prompt configuration
├── git/
│ ├── gitconfig # Global Git config
│ └── gitignore_global # Global ignore rules
├── lib/
│ ├── backup.sh # Backup helpers
│ ├── common.sh # Shared helpers
│ ├── doctor.sh # Environment health checks
│ ├── install.sh # Dotfile installation logic
│ ├── macos.sh # macOS defaults
│ ├── packages.sh # Package installation helpers
│ └── update.sh # Update helpers
├── nvim/
│ └── init.lua # Minimal Neovim config
├── zsh/
│ ├── aliases.zsh
│ ├── completions.zsh
│ ├── functions.zsh
│ ├── path.zsh
│ └── zshrc
├── .editorconfig
├── .shellcheckrc
├── .shfmt
├── Brewfile
├── install.sh
├── mise.toml
└── README.md
The bootstrap script expects:
bashgitcurl
On macOS, Homebrew will be installed if it is missing.
On Linux, package installation is intentionally conservative. The script will detect Homebrew/Linuxbrew when available, but it will not mutate system package managers like apt, dnf, or pacman unless you extend it yourself.
Clone the repo:
git clone git@github.com:YOUR_GITHUB_USERNAME/dotfiles.git ~/.dotfiles
cd ~/.dotfilesRun the installer:
./install.shPreview changes without applying them:
./install.sh --dry-runInstall dotfiles without installing packages:
./install.sh --skip-packagesForce relinking of managed files:
./install.sh --forceAfter installation, restart your shell:
exec zshThe installer links the following files into your home directory:
~/.zshrc -> zsh/zshrc
~/.config/zsh/* -> zsh/*
~/.gitconfig -> git/gitconfig
~/.gitignore_global -> git/gitignore_global
~/.config/starship.toml -> config/starship.toml
~/.config/direnv/direnvrc -> config/direnvrc
~/.config/mise/config.toml -> mise.toml
~/.config/nvim/init.lua -> nvim/init.lua
~/.local/bin/dot -> bin/dot
If a target file already exists and is not already managed by this repo, it is moved to:
~/.dotfiles-backups/YYYYMMDD-HHMMSS/
Run a health check:
dot doctorUpdate packages, tools, and the dotfiles repo:
dot updateRe-run the installer:
dot installApply macOS defaults:
dot macosShow help:
dot helpPackages are declared in Brewfile.
Install or update packages:
brew bundle --file ~/.dotfiles/BrewfileDump your currently installed Homebrew packages for review:
brew bundle dump --file Brewfile.local --describe --forceDo not blindly replace the main Brewfile with a dump. Review additions first.
Global tools are declared in mise.toml.
Install configured tools:
mise installCheck active versions:
mise currentAdd a new tool:
mise use --global node@lts
mise use --global python@latestProject-specific versions should live in the project repository, not here:
cd ~/Code/example-project
mise use node@22The default Git config intentionally avoids hardcoded personal identity values.
Create local identity files outside this repo:
mkdir -p ~/.config/gitPersonal identity:
cat > ~/.config/git/personal.gitconfig <<'EOF_PERSONAL'
[user]
name = Your Name
email = you@example.com
EOF_PERSONALWork identity:
cat > ~/.config/git/work.gitconfig <<'EOF_WORK'
[user]
name = Your Name
email = you@company.com
EOF_WORKThe included Git config supports conditional includes for common paths:
[includeIf "gitdir:~/Code/personal/"]
path = ~/.config/git/personal.gitconfig
[includeIf "gitdir:~/Code/work/"]
path = ~/.config/git/work.gitconfigEdit git/gitconfig to match your preferred folder structure.
Do not store secrets in this repository.
Recommended options:
- GitHub CLI:
gh auth login - SSH keys stored under
~/.ssh - 1Password CLI, Bitwarden CLI, or your preferred secret manager
- Environment variables loaded through
direnvfor project-specific secrets
Use ignored local files for machine-specific customizations:
~/.config/zsh/local.zsh
~/.config/git/local.gitconfig
~/.config/mise/local.toml
These are intentionally not tracked.
- Create a branch.
- Make changes.
- Run checks locally:
dot doctor
shellcheck install.sh bin/dot lib/*.sh
shfmt -d install.sh bin/dot lib/*.sh
zsh -n zsh/*.zsh- Commit and push.
- Open a pull request.
Create a new GitHub repository named dotfiles, then push:
cd ~/.dotfiles
git init
git add .
git commit -m "Initial modern dotfiles setup"
git branch -M main
git remote add origin git@github.com:YOUR_GITHUB_USERNAME/dotfiles.git
git push -u origin mainGitHub Codespaces can automatically apply a dotfiles repository when configured in your Codespaces settings. This repo includes a standard install.sh, which Codespaces can detect and run.
Good additions:
lib/docker.shfor Docker Desktop or Colima setuplib/security.shfor SSH key checks, GPG, or signing setuplib/fonts.shfor developer fontslib/vscode.shfor VS Code extensionslib/devcontainers.shfor container tooling
Avoid:
- Hardcoded access tokens
- Work-only credentials
- Machine-specific paths in shared config
- Destructive
rm -rfbehavior - Installing abandoned plugin managers or vendored submodules
Run:
dot doctorCommon fixes:
brew update
brew bundle --file ~/.dotfiles/Brewfile
mise doctor
mise install
exec zshIf a link looks wrong, rerun:
./install.sh --forceBackups are stored in ~/.dotfiles-backups/.