Every developer has lived through this: a new laptop arrives, and you spend an entire day (or two) installing tools, configuring your shell, cloning repos, setting up SSH keys, installing language runtimes, and getting your terminal to look the way you like it. By the time you are productive, you have lost 8-16 hours of work.
It does not have to be this way. A properly automated development environment setup gets you from a bare machine to full productivity in under 30 minutes – with a single command.
This guide covers four complementary approaches to environment automation, from simple dotfiles management to full machine-as-code with Nix. Choose the level of complexity that matches your needs.
Why Automate Your Dev Environment?
The time investment in automation pays dividends across multiple scenarios:
- New machine setup: Hours become minutes
- Machine failure: Recover your full environment from backup + one script
- Team onboarding: New developers are productive on day one
- Consistency: Your work machine, personal machine, and CI all behave identically
- Documentation: Your automation IS your documentation – it describes your environment precisely
The compound benefit is significant. Over a career, you will set up dozens of machines. Automation turns each instance from a day of work into a coffee break.
Level 1: Dotfiles Management
Dotfiles are the configuration files that define your development
environment: .zshrc, .gitconfig,
.vimrc, editor settings, terminal config, and
tool-specific configurations. Managing them in a git repository is
the minimum viable environment automation.
Setting Up a Dotfiles Repository
Step 1: Create the repository
Create a repository (public or private) on GitHub to store your configuration files.
Step 2: Organize your dotfiles
A clean structure separates concerns:
dotfiles/
git/
.gitconfig
.gitignore_global
shell/
.zshrc
.zprofile
aliases.zsh
functions.zsh
terminal/
ghostty/config
starship.toml
editor/
vscode/settings.json
vscode/keybindings.json
vscode/extensions.txt
tools/
.ripgreprc
atuin/config.toml
install.sh
Brewfile
Step 3: Create a symlink installer
The install.sh script creates symlinks from your
home directory to the repository:
#!/bin/bash
set -euo pipefail
DOTFILES="$HOME/dotfiles"
# Git
ln -sf "$DOTFILES/git/.gitconfig" "$HOME/.gitconfig"
ln -sf "$DOTFILES/git/.gitignore_global" "$HOME/.gitignore_global"
# Shell
ln -sf "$DOTFILES/shell/.zshrc" "$HOME/.zshrc"
# Terminal
mkdir -p "$HOME/.config/ghostty"
ln -sf "$DOTFILES/terminal/ghostty/config" "$HOME/.config/ghostty/config"
# Starship prompt
ln -sf "$DOTFILES/terminal/starship.toml" "$HOME/.config/starship.toml"
echo "Dotfiles installed."Step 4: Track VS Code extensions
Export your extensions list:
code --list-extensions > dotfiles/editor/vscode/extensions.txtRestore them on a new machine:
cat dotfiles/editor/vscode/extensions.txt | xargs -L 1 code --install-extensionDotfiles Best Practices
- Keep secrets out. Never commit SSH keys, API tokens, or passwords. Use environment variables or a secrets manager.
- Make it idempotent. Running
install.shtwice should produce the same result as running it once. - Document deviations. If something requires manual setup, note it in a README.
- Test periodically. Spin up a fresh VM and run your installer to verify it still works.
Level 2: Package Management with Brewfile
macOS developers can declare all installed software in a
Brewfile – a manifest of everything Homebrew should
install.
Creating a Brewfile
# Brewfile
# Terminal tools
brew "git"
brew "gh"
brew "starship"
brew "zoxide"
brew "atuin"
brew "eza"
brew "fd"
brew "ripgrep"
brew "jq"
brew "yq"
brew "bat"
brew "just"
brew "lefthook"
# Development
brew "node"
brew "python@3.12"
brew "go"
brew "rustup"
brew "docker"
# Database tools
brew "postgresql@16"
brew "redis"
brew "sqlite"
# Applications (casks)
cask "ghostty"
cask "visual-studio-code"
cask "docker"
cask "fork" # Git GUI
cask "tableplus" # Database GUI
cask "bruno" # API testing
cask "1password"
cask "raycast"
cask "orbstack" # Docker Desktop alternative
# Fonts
cask "font-jetbrains-mono"
cask "font-fira-code"
# VS Code extensions (via vscode-extensions tap)
vscode "github.copilot"
vscode "github.copilot-chat"
vscode "esbenp.prettier-vscode"
vscode "dbaeumer.vscode-eslint"
vscode "eamodio.gitlens"Installing Everything
# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install everything from Brewfile
brew bundle --file=~/dotfiles/BrewfileOne command installs every tool, application, and font you need. On a fresh Mac with decent internet, this takes 10-15 minutes to complete.
Keeping the Brewfile Current
When you install new tools, add them to the Brewfile. To capture your current state:
brew bundle dump --file=~/dotfiles/Brewfile --forceThis regenerates the Brewfile from what is currently installed, so you never forget to add something.
Level 3: Bootstrap Script
Combine dotfiles and Brewfile into a single bootstrap script that takes a bare machine to fully configured.
The Bootstrap Script
#!/bin/bash
# bootstrap.sh - Full machine setup from scratch
set -euo pipefail
echo "=== Starting machine bootstrap ==="
# 1. Install Homebrew
if ! command -v brew &> /dev/null; then
echo "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/opt/homebrew/bin/brew shellenv)"
fi
# 2. Clone dotfiles
if [ ! -d "$HOME/dotfiles" ]; then
echo "Cloning dotfiles..."
git clone https://github.com/YOUR_USERNAME/dotfiles.git "$HOME/dotfiles"
fi
# 3. Install packages
echo "Installing packages from Brewfile..."
brew bundle --file="$HOME/dotfiles/Brewfile"
# 4. Install dotfiles (symlinks)
echo "Linking dotfiles..."
bash "$HOME/dotfiles/install.sh"
# 5. Set shell to zsh (if not already)
if [ "$SHELL" != "/bin/zsh" ]; then
chsh -s /bin/zsh
fi
# 6. Install Node.js LTS via fnm/nvm
if command -v fnm &> /dev/null; then
fnm install --lts
fnm default lts-latest
fi
# 7. Install Rust toolchain
if command -v rustup &> /dev/null; then
rustup-init -y --default-toolchain stable
fi
# 8. Configure Git
git config --global user.name "Your Name"
git config --global user.email "your@email.com"
# 9. Set macOS defaults
echo "Setting macOS preferences..."
# Show hidden files in Finder
defaults write com.apple.finder AppleShowAllFiles -bool true
# Show path bar in Finder
defaults write com.apple.finder ShowPathbar -bool true
# Disable press-and-hold for accent characters (faster key repeat)
defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false
# Fast key repeat rate
defaults write NSGlobalDomain KeyRepeat -int 2
defaults write NSGlobalDomain InitialKeyRepeat -int 15
# 10. Create standard project directories
mkdir -p "$HOME/projects"
mkdir -p "$HOME/scratch"
echo "=== Bootstrap complete! ==="
echo "Please restart your terminal for all changes to take effect."Running the Bootstrap
On a fresh machine, the entire setup is:
# Install Xcode command line tools (prerequisite)
xcode-select --install
# Download and run bootstrap
curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/dotfiles/main/bootstrap.sh | bashFrom bare metal to fully configured in 15-30 minutes, unattended.
Level 4: Dev Containers for Project Environments
While dotfiles and Brewfile handle your personal machine, Dev Containers handle per-project development environments. This ensures every developer on the team has identical tooling regardless of their local setup.
What Dev Containers Provide
A Dev Container is a Docker container pre-configured with everything a project needs: - Correct language runtime version - All required system dependencies - Editor extensions and settings - CLI tools specific to the project - Database clients and utilities
Creating a Dev Container
Add .devcontainer/devcontainer.json to your
project:
{
"name": "My Project",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/aws-cli:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"github.copilot"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
},
"postCreateCommand": "npm install",
"forwardPorts": [3000, 5432],
"mounts": [
"source=${localEnv:HOME}/.ssh,target=/home/node/.ssh,type=bind,readonly"
]
}Benefits for Teams
- Zero onboarding friction. Clone repo, open in container, start coding. No “follow the 47-step setup guide.”
- Version pinning. The container specifies exact tool versions. No “it works on Node 18 but not 20” surprises.
- Isolation. Project A needs Python 3.11, project B needs Python 3.12. No conflicts.
- CI parity. Run CI inside the same container your developers use locally.
Level 5: Nix for Reproducible Environments (Advanced)
Nix takes reproducibility to its logical extreme. Every dependency, every tool, every library version is pinned cryptographically. Two machines with the same Nix configuration are byte-for-byte identical in their development tooling.
Why Nix?
- Reproducibility guarantee. If it builds on your machine, it builds on any machine with the same Nix expression.
- Multiple versions coexist. Python 3.10, 3.11, and 3.12 all installed simultaneously without conflicts.
- Rollbacks. Made a bad configuration change? Roll back to the previous generation instantly.
- Declarative. Your entire system is described in code.
Basic Nix Dev Shell
For per-project environments, use flake.nix:
{
description = "Project dev environment";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system}; in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs_20
nodePackages.pnpm
postgresql_16
redis
just
gh
];
shellHook = ''
echo "Dev environment loaded: Node $(node --version), PostgreSQL $(psql --version | head -1)"
'';
};
}
);
}Enter the environment with nix develop. Exit with
exit. Your global system is untouched.
Nix Tradeoffs
Pros: Maximum reproducibility, no conflicts, declarative, rollbacks. Cons: Steep learning curve, slow initial builds, large disk usage, documentation can be sparse, macOS support has friction points.
Recommendation: Use Nix if your team has a champion willing to maintain it. Otherwise, Brewfile + Dev Containers provides 90% of the benefit with 20% of the complexity.
Recommended Approach by Team Size
| Team Size | Recommended Approach |
|---|---|
| Solo developer | Dotfiles + Brewfile + bootstrap script |
| Small team (2-5) | Above + Dev Containers for project environments |
| Medium team (5-20) | Dev Containers (mandatory) + shared dotfiles template |
| Large team (20+) | Dev Containers + Nix (for build reproducibility) + internal developer platform |
Maintenance and Evolution
Keep Automation Current
- After installing any new tool: Add it to Brewfile and commit
- After changing any config: Update dotfiles and commit
- Quarterly: Test bootstrap on a fresh VM (or use GitHub Codespaces as a test)
- When onboarding: Ask new developers what was missing or broken
Version Your Environment
Tag your dotfiles repository when you make major changes. If a new tool breaks something, you can check out the previous tag and restore your working environment.
Document the Escape Hatches
Some things resist automation: macOS system preferences that
require clicking through UI, proprietary software that requires
manual license activation, hardware-specific drivers. Document these
in a MANUAL_STEPS.md file in your dotfiles repo – the
goal is that this file gets shorter over time.
Frequently Asked Questions
How long does it take to set up this automation?
Initial setup takes 2-4 hours to create your dotfiles repo, Brewfile, and bootstrap script. The ongoing maintenance is 5-10 minutes per week (committing new tool additions). The time saved per machine setup (8-16 hours) pays back the investment immediately.
Should I make my dotfiles public or private?
Public is fine for most developers – dotfiles rarely contain secrets. The benefit of public dotfiles is that others can learn from your configuration, and you can reference other developers’ setups for inspiration. Keep secrets in environment variables, not dotfiles.
How do I handle work vs personal machine differences?
Use conditional logic in your shell config:
if [ -f "$HOME/.work" ]; then
# Work-specific settings
export AWS_PROFILE=work
source "$HOME/.work-aliases"
else
# Personal settings
export AWS_PROFILE=personal
fiCreate a .work file on work machines. Your dotfiles
adapt automatically.
Can I use this with Windows?
Yes, with modifications. WSL2 provides a Linux environment where most of this guide applies directly. Replace Brewfile with a combination of winget (Windows apps) and apt/nix inside WSL2. The Dev Container approach works identically on Windows with VS Code.
What about SSH keys and credentials?
Never store private keys or credentials in dotfiles. Use: -
1Password CLI / SSH agent: 1Password can serve as
your SSH agent, providing keys on demand - GitHub
CLI: gh auth login handles Git authentication
without SSH keys - Age encryption: Encrypt
sensitive files in your dotfiles repo with age, decrypt during
bootstrap
How do I test my bootstrap script?
Use a disposable VM or Docker container:
docker run -it --rm ubuntu:22.04 /bin/bash
# Then run your bootstrap script inside the containerGitHub Codespaces also works well for testing – create a codespace from your dotfiles repo and verify everything installs correctly.
Related Articles:
On devtoolkit.io: - The Ultimate Developer Workflow Guide 2026 — The complete workflow your automated environment supports - Best Git Clients 2026 — Include Fork or GitKraken in your Brewfile (see our comparison) - Best API Testing Tools 2026 — Add Bruno to your automated setup for API testing - GitKraken Review 2026 — One of the Git GUIs to include in your Brewfile
On aitoolranked.com (sister site): - Best AI Code Assistants 2026 — Automate your AI coding tool setup alongside your dev environment
On desksetuppro.com (sister site): - Complete Home Office Setup Guide: $500 Budget — Automate your software, then build the physical workspace - Complete Home Office Setup Guide: $2000 Budget — Premium physical setup for your automated dev environment
Last updated: May 2026.
[AFFILIATE DISCLOSURE: This post contains affiliate links. If you purchase through our links, we may earn a commission at no extra cost to you.]