DEV Community

Anjan
Anjan

Posted on

My One-Command macOS Dev Setup: Ruby, Elixir, Zig, and Everything in Between

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:

  1. One command to install everything. No "Step 7: now manually configure X."
  2. One theme everywhere. If my editor, terminal, prompt, and multiplexer all look different, my brain wastes cycles adjusting. Tokyo Night everywhere.
  3. Keyboard-first. If I have to reach for the mouse, something is wrong.
  4. Modular by concern. Shell config split by domain (Rails, Elixir, terminal tools), not dumped into one 2,000-line .zshrc.
  5. 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)

Repo: github.com/AnjanJ/dotfiles


One-Command Install

git clone https://github.com/AnjanJ/dotfiles.git ~/dotfiles
cd ~/dotfiles
bash install.sh
Enter fullscreen mode Exit fullscreen mode

That's it. The script walks through 11 steps:

  1. Installs Homebrew (if missing)
  2. Runs brew bundle install — 72 CLI packages, 106 cask apps, 110 VS Code extensions
  3. Creates ~/.config/ directories
  4. Backs up your existing configs to ~/.dotfiles_backup_YYYYMMDD_HHMMSS/
  5. Sets up mise and installs 7 language runtimes
  6. Symlinks every config file into place
  7. Installs TPM (tmux plugin manager)
  8. Bootstraps Neovim (lazy.nvim auto-installs plugins on first launch)
  9. Copies Zed settings, tasks, and snippets
  10. Sets zsh as default shell
  11. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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"]
}
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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:

  • Neovimtokyo-night-theme.lua plugin
  • Ghosttytheme = tokyonight_night (built-in)
  • Starship — custom [palettes.tokyonight] section with all 16 colors
  • Zellij — hand-mapped themes/tokyo-night.kdl with 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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Want a different theme? Change it in 5 places:

  1. starship.toml — the [palettes] section
  2. ghostty/config — the theme line
  3. nvim/lua/plugins/ — swap the theme plugin
  4. zellij/themes/ — edit the KDL file
  5. .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
Enter fullscreen mode Exit fullscreen mode

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.

github.com/AnjanJ/dotfiles


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)