Personal configuration files for macOS, Linux, and FreeBSD. Uses a bare git repo approach so dotfiles live directly in $HOME without symlinks.
On a fresh machine, run:
curl -Lks https://raw.githubusercontent.com/jimray/config/main/.dotfiles-init.sh | /bin/shThis will:
- Install git if needed
- Clone the dotfiles as a bare repo to
~/.cfg - Check out config files (backing up any conflicts)
- Optionally run the bootstrap script
| File | Purpose |
|---|---|
.zshrc |
Main shell config (sources OS-specific files) |
.zshrc_macos / .zshrc_linux |
OS-specific shell settings |
.zfunc |
Shell functions, like vm (see below) |
.vimrc |
Vim/Neovim configuration |
.tmux.conf |
tmux with plugins (resurrect, continuum, vim-navigator) |
.gitconfig |
Git aliases and settings |
.config/starship.toml |
Starship prompt theme |
.config/nvim/ |
Neovim configuration |
.Brewfile |
Homebrew packages (work) |
.Brewfile.personal |
Homebrew packages (personal) |
.bootstrap.sh |
Full system setup script |
.lima/_config/default.yaml |
Auto-provisions dotfiles in Lima VMs |
.tool-versions |
mise-managed runtime versions |
After setup, use the config alias instead of git:
config status
config add .vimrc
config commit -m "Update vim config"
config pushThe bootstrap script (.bootstrap.sh) installs everything needed for a development environment:
- macOS: Homebrew, CLI tools, app preferences (Dock, Finder, Safari)
- Linux: apt packages, eza, GitHub CLI, starship prompt
- FreeBSD: pkg packages
Plus cross-platform setup:
- tmux plugin manager (tpm)
- Vim plugins (vim8-style packages)
- Neovim symlinks
- mise (runtime version manager)
- Shell: zsh + Starship prompt
- Editor: Neovim (with vim as fallback)
- Terminal: tmux + iTerm2 (macOS)
- Search: fzf, ripgrep
- File listing: eza
- File preview: bat
- Directory jumping: zoxide
- Runtime versions: mise
zoxide replaces cd with a frecency-based directory jumper. Use z instead of cd — it learns your most-visited directories and lets you jump with partial names.
eza replaces ls with icons, git status, and better formatting. Several aliases are configured:
| Alias | Description |
|---|---|
ls |
Default listing with icons |
ll |
Long format with git status, timestamps, and groups |
lt |
Tree view |
lg |
Grid view |
xls |
Escape hatch to system ls |
Two functions in .zfunc/ combine ripgrep, fzf, bat, and Neovim into a fast file-picking workflow. Both require bat for previews.
vf — fuzzy file picker with preview. Launches fzf with a syntax-highlighted bat preview of each file; opens the selection in Neovim.
vrg — search file contents, preview matches, open at the right line. Runs ripgrep across the current directory, passes results to fzf with bat highlighting the matched line, then opens the file in Neovim at the correct line number. Useful as a terminal-native alternative to an IDE's global search.
serve [port] — starts a Python HTTP server for the current directory, defaulting to port 8000. If that port is taken, it increments automatically up to 10 times before giving up.
serve # starts at 8000, or next available
serve 3000 # starts at 3000, or next availablemise (mise-en-place) manages runtime versions for Node.js, Go, Deno, and other tools. It reads .tool-versions in the current directory (or $HOME) to determine which versions to use, making it easy to keep consistent versions across machines and VMs.
mise is activated in .zshenv so it's available in all shell sessions:
eval "$(mise activate zsh)"The bootstrap script installs mise automatically on Linux and macOS.
# Install all runtimes listed in .tool-versions
mise install
# Install a specific tool at a specific version
mise use --global node@lts
mise use --global [email protected]
# Check latest available version
mise latest node
mise latest go
# List installed tools and their versions
mise list
# Check what's active in the current directory
mise currentThe .tool-versions file in $HOME defines the global defaults:
deno 2.6.0
go 1.25.5
node 25.2.1
You can also drop a .tool-versions in any project directory to override versions locally — mise will pick it up automatically when you cd into that directory.
Since dotfiles are provisioned automatically in new VMs, .tool-versions will already be present. From inside the VM:
# Install everything in .tool-versions at once
mise install
# Or install Node specifically
mise use --global node@ltsIf you're still in a bash session (before chsh has switched you to zsh), activate mise manually first:
eval "$(~/.local/bin/mise activate bash)"Lima provides Linux VMs on macOS (and Linux). The included .lima/_config/default.yaml automatically provisions dotfiles on every new VM.
brew install limaCreate a VM with minimal config (recommended)
limactl create --name myvm ~/.config/lima/templates/minimal.yamlStart the VM
limactl start myvmOr use Lima's default template (verbose config)
limactl create --name myvmlimactl start myvmShell into the VM
limactl shell myvmStop and delete
limactl stop myvmlimactl delete myvmList all VMs
limactl listWhen any Lima VM starts, the config in .lima/_config/default.yaml runs automatically:
- Checks if
~/.cfgexists (dotfiles already set up) - If not, downloads and runs the dotfiles init script
- Runs the bootstrap to install tools
No manual setup needed - just limactl create and your dev environment is ready.
By default, Lima mounts your home directory as read-only. To make directories writable for a specific VM, edit ~/.lima/<vm-name>/lima.yaml and add a mounts section:
mounts:
- location: "~"
writable: false
- location: "~/Projects"
writable: trueThen restart the VM for changes to take effect:
limactl stop myvm && limactl start myvmA vm shell function enables running commands in a Lima VM without explicitly SSHing. It looks for a .vm file in your current directory to determine which VM to use (or asks which vm to use and optionally saves it to .vm).
It even works for interactive tools like Claude.
# First time in a new project (no .vm file)
vm ls
# → Available VMs:
# 1) work
# 2) personal
# Which VM? (number or name): 2
# Save 'personal' to .vm file? (y/n): y
# In a project directory with a .vm file
vm claude # Run Claude in the associated VM
vm npm test # Run npm test in the VM
vm git status # Run git in the VMThis pattern enables sandboxed execution of AI tools (like Claude Code) or experimental code without replicating your entire development environment in the VM:
- Security: AI tools run isolated from your SSH keys, API tokens, and credentials
- Flexibility: Use your local editor, git, and familiar tools
- Progressive isolation: Start minimal, add VM-specific tooling only as needed
- Low friction: The
.vmfile acts as a simple project-level configuration
Add .vm to your global gitignore since VM names are machine-specific:
echo ".vm" >> ~/.gitignoreFor machine-specific settings that shouldn't be committed:
.zshrc_local- Shell customizations.gitconfig.local- Git identity, work vs personal email