Every developer knows the pain. You get a new machine — or worse, you nuke your old one — and spend the next two days installing Homebrew, cloning repos, configuring editors, setting up language versions, and wondering why your terminal looks wrong. By the time you're actually writing code again, it's Wednesday.
I got tired of that. So I built a dotfiles repo that turns a fresh macOS install into a fully configured development environment with three commands. One window manager. Two editors. Two terminal multiplexers. Seven languages. 288 packages. All wired together with a unified color theme.
This is how it works, and more importantly, why it's built this way.
Why I Built This
I've been a Rails developer for 9+ years. Over that time, my setup grew organically — a Vim plugin here, a shell alias there, a theme that only worked in one app. When I saw DHH's Omakase approach applied to developer tooling (everything opinionated, everything in one place), something clicked. That philosophy already shaped how I write Rails apps. Why not my entire dev environment?
I started over with five principles:
- One command to install everything. No "Step 7: now manually configure X."
- One theme everywhere. If my editor, terminal, prompt, and multiplexer all look different, my brain wastes cycles adjusting. Tokyo Night everywhere.
- Keyboard-first. If I have to reach for the mouse, something is wrong.
-
Modular by concern. Shell config split by domain (Rails, Elixir, terminal tools), not dumped into one 2,000-line
.zshrc. - Idempotent and safe. Run the installer twice and nothing breaks. Existing configs get backed up with timestamps.
I think of myself as an integrator — I prefer to find the best dev tools rather than build new tools from scratch, I find the best ones and wire them together. This repo is that philosophy made concrete.
The Stack at a Glance
| Tool | What | Why |
|---|---|---|
| Aerospace | Tiling window manager | i3-like on macOS, pure keyboard control |
| Ghostty | GPU-accelerated terminal | Fast, minimal, built-in Tokyo Night theme |
| Neovim | Terminal editor | AstroNvim + 17 plugin configs, deep Rails/Elixir support |
| Zed | GUI editor | When you need a full IDE — 24 tasks, 68 snippets, 7 LSPs |
| tmux | Terminal multiplexer | 50K line scrollback, Ctrl+A prefix, TPM plugins |
| Zellij | Alt multiplexer | Rust-based, WebAssembly plugins, session persistence |
| Starship | Shell prompt | 16x faster than Spaceship, shows Ruby/Elixir/Node/Zig versions |
| mise | Version manager | One tool for Ruby, Elixir, Node, Python, Go, Rust (replaces rbenv/nvm/pyenv) |
One-Command Install
git clone https://github.com/AnjanJ/dotfiles.git ~/dotfiles
cd ~/dotfiles
bash install.sh
That's it. The script walks through 11 steps:
- Installs Homebrew (if missing)
- Runs
brew bundle install— 72 CLI packages, 106 cask apps, 110 VS Code extensions - Creates
~/.config/directories - Backs up your existing configs to
~/.dotfiles_backup_YYYYMMDD_HHMMSS/ - Sets up mise and installs 7 language runtimes
- Symlinks every config file into place
- Installs TPM (tmux plugin manager)
- Bootstraps Neovim (lazy.nvim auto-installs plugins on first launch)
- Copies Zed settings, tasks, and snippets
- Sets zsh as default shell
- Runs health check (optional)
The key design choice: symlinks, not copies. Every config file lives in the git repo. Edit your Zed settings in ~/dotfiles/.config/zed/settings.json, and the change is immediately tracked by git. No manual syncing.
# What the symlink setup looks like
ln -sf ~/dotfiles/.zshrc ~/.zshrc
ln -sf ~/dotfiles/.config/starship.toml ~/.config/starship.toml
ln -sf ~/dotfiles/.config/aerospace/aerospace.toml ~/.config/aerospace/aerospace.toml
# ... and 15+ more
Already have configs? They're safe. The installer backs up everything to a timestamped directory before touching anything. And since the script is idempotent, you can re-run it after pulling new changes — it'll update symlinks without clobbering your data.
Modular Shell: Why 4 Files Instead of 1
Most dotfiles repos dump everything into a single .zshrc. Mine splits it into four files by concern:
| File | Lines | What's in it |
|---|---|---|
.zshrc |
171 | Core: Starship, mise, PATH, project aliases |
.zshrc-dhh-additions |
644 | Rails: r, rs, rc, RSpec, Bundle, Git, database helpers |
.zshrc-elixir-additions |
340 | Phoenix: Mix, IEx, LiveView, Ecto shortcuts |
.zshrc-terminal-enhancements |
201 | CLI: fzf, zoxide, bat, eza, ripgrep integration |
| Total | 1,356 |
The magic is in how .zshrc loads them:
# DHH-inspired Rails developer setup
if [ -f ~/.zshrc-dhh-additions ]; then
source ~/.zshrc-dhh-additions
fi
# Modern terminal enhancements
if [ -f ~/.zshrc-terminal-enhancements ]; then
source ~/.zshrc-terminal-enhancements
fi
# Elixir & Phoenix developer setup
if [ -f ~/.zshrc-elixir-additions ]; then
source ~/.zshrc-elixir-additions
fi
Don't use Elixir? Comment out one line. Want to add Rust-specific aliases? Create .zshrc-rust-additions and source it. The pattern is self-documenting.
The DHH additions file alone has 16 organized sections — Rails aliases, Bundle shortcuts, Git workflow, Docker Compose, Heroku, database helpers, credential management, and even an alias discovery system:
# Core Rails commands — type less, ship more
alias r='bin/rails'
alias rs='bin/rails server'
alias rc='bin/rails console'
alias rgen='bin/rails generate'
Zed Editor: My Secret Weapon
I use Neovim for quick terminal edits and Zed for longer sessions. Zed is absurdly fast (it's written in Rust), has native terminal integration, and lets me configure everything in JSON without plugins.
7 LSPs configured out of the box: ruby-lsp (with RuboCop), TypeScript, ESLint, elixir-ls, tailwindcss, zls (Zig), and Prettier. Each language has its own formatter, tab size, and linting rules.
24 tasks accessible from Cmd+Shift+P:
{
"label": "RSpec: Run current file",
"command": "bundle exec rspec $ZED_FILE",
"use_new_terminal": false,
"tags": ["ruby-test"]
},
{
"label": "RSpec: Run current line",
"command": "bundle exec rspec $ZED_FILE:$ZED_ROW",
"use_new_terminal": false,
"tags": ["ruby-test"]
}
I have tasks for RSpec (4), Rails (5), RuboCop (2), Elixir/Mix (5), npm (2), and Zig (6). I never leave the editor to run tests.
68 snippets across three languages — Ruby (31), Zig (24), and ERB (13). These are the patterns I type every day: frozen_string_literal headers, RSpec describe/context/it blocks, Rails controller actions, Zig error handling, ERB form helpers.
The Zed config uses symlinks too. Edit snippets or tasks in ~/dotfiles/.config/zed/, commit, push. Any machine I clone this repo to gets the same editor experience.
Window Management: Keyboard-First with Aerospace
Aerospace is an i3-like tiling window manager for macOS. Windows automatically tile. No dragging, no resizing with a mouse.
I use Ctrl+Shift as the primary modifier (instead of Cmd) because it works reliably across international keyboards:
# Focus windows with vim keys
ctrl-shift-h = 'focus left'
ctrl-shift-j = 'focus down'
ctrl-shift-k = 'focus up'
ctrl-shift-l = 'focus right'
# Move windows
ctrl-alt-h = 'move left'
ctrl-alt-j = 'move down'
ctrl-alt-k = 'move up'
ctrl-alt-l = 'move right'
9 workspaces organized by purpose. Each major app gets a dedicated hotkey:
| Hotkey | App | Workspace |
|---|---|---|
Ctrl+Shift+C |
Chrome | 1 (Work) |
Ctrl+Shift+Z |
Zed | 2 (Code) |
Ctrl+Shift+W |
Warp | 3 (Terminal) |
Ctrl+Shift+F |
Firefox | 5 (Personal) |
Ctrl+Shift+G |
Ghostty | 7 (Personal Terminal) |
Ctrl+Shift+O |
Obsidian | 8 (PKM) |
Ctrl+Shift+P |
1Password | 9 (Utils) |
System apps (Calculator, System Settings, Finder, Zoom) automatically float instead of tiling. The config handles all of this declaratively — no scripts, no daemon, just TOML.
Keeping It Alive: Update and Health Check
Dotfiles that rot are worse than no dotfiles. I built two scripts to keep everything current.
bash update.sh pulls the latest changes, updates Homebrew, refreshes symlinks, upgrades mise runtimes, updates tmux plugins, and reloads configs — all in one command.
bash scripts/health-check.sh validates that everything still works. It checks 11 categories:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Core Tools
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Homebrew: installed (Homebrew 4.x.x)
✓ git: installed (git version 2.x.x)
✓ mise: installed (2025.x.x)
✓ starship: installed (starship 1.x.x)
It checks core tools, terminals, window management, editors, shell config symlinks, all 7 language runtimes, CLI utilities, databases, framework tools, and custom scripts. Color-coded output with pass/fail/warning counts at the end.
After a macOS update breaks something (and it will), health-check.sh tells you exactly what needs fixing.
Tokyo Night Everywhere
Cognitive load matters. When your editor uses one color scheme, your terminal uses another, and your prompt uses a third, your eyes constantly readjust. It's a small thing, but it adds up over a full day.
Every tool in this setup uses Tokyo Night:
-
Neovim —
tokyo-night-theme.luaplugin -
Ghostty —
theme = tokyonight_night(built-in) -
Starship — custom
[palettes.tokyonight]section with all 16 colors -
Zellij — hand-mapped
themes/tokyo-night.kdlwith RGB values - tmux — tokyo-night plugin via TPM
Five config files, one visual language. The Starship config alone defines 20 color values to match:
[palettes.tokyonight]
background = "#1a1b26"
foreground = "#c0caf5"
red = "#f7768e"
green = "#9ece6a"
blue = "#7aa2f7"
purple = "#bb9af7"
cyan = "#7dcfff"
orange = "#ff9e64"
How to Adapt This for Yourself
This repo is opinionated — it reflects how I work. But the modular design makes it easy to make it yours.
Don't use Elixir? Delete .zshrc-elixir-additions and remove the source line from .zshrc. Done.
Want different languages? Edit .config/mise/config.toml:
[tools]
ruby = "3.4.5"
elixir = "latest"
node = "latest"
python = "3"
go = "latest"
rust = "latest"
# Add or remove languages here
Want a different theme? Change it in 5 places:
-
starship.toml— the[palettes]section -
ghostty/config— thethemeline -
nvim/lua/plugins/— swap the theme plugin -
zellij/themes/— edit the KDL file -
.tmux.conf— change the theme plugin
Want your own Zed snippets? Add JSON files to .config/zed/snippets/. The installer symlinks the entire directory.
Fork it. Remove what you don't need. Add what you do. The install script handles everything — your fork is one bash install.sh away from working on any Mac.
git clone https://github.com/YOUR_USERNAME/dotfiles.git ~/dotfiles
cd ~/dotfiles
bash install.sh
What's Next
I keep iterating on this. Recent additions include Zig language support (6 Zed tasks, 24 snippets, ZLS configured), browser window cycling across workspaces, and AI model configuration in Zed (Claude, GPT-4o, Gemini — all ready to use).
If you want the full details — every keybinding, every alias, every snippet — the README is comprehensive.
Star it if it's useful. Fork it if you want to make it yours. And if you have a setup trick I'm missing, I'd love to hear about it.
I'm a Rails engineer with 9+ years of experience, currently open to remote opportunities. You can find me on GitHub and LinkedIn.
Top comments (0)