Personal dotfiles for macOS, managed with traditional symlinks and automated sync.
| Application | Config Location | Description |
|---|---|---|
| AeroSpace | aerospace/ |
Tiling window manager for macOS |
| Ghostty | ghostty/ |
GPU-accelerated terminal emulator |
| WezTerm | wezterm/ |
Cross-platform terminal emulator |
| Yazi | yazi/ |
Terminal file manager |
| Neovim | nvim/ |
LazyVim-based Neovim configuration |
| Zsh | zsh/ |
Shell configuration with aliases and functions |
| Homebrew | Brewfile |
All installed packages and casks |
# Clone the repo
git clone https://github.com/alexfazio/dotfiles.git ~/.dotfiles
# Run install script
cd ~/.dotfiles && ./install.sh- Creates symlinks from
~/.config/*to~/.dotfiles/* - Installs Homebrew packages from Brewfile
- Installs gitleaks and pre-commit for secret protection
- Sets up hourly auto-sync via launchd
- Creates
~/.secrets.envfrom template
Files live in ~/.dotfiles/ and are symlinked to their expected locations:
~/.dotfiles/aerospace/aerospace.toml → ~/.config/aerospace/aerospace.toml
~/.dotfiles/ghostty/config → ~/.config/ghostty/config
~/.dotfiles/wezterm/wezterm.lua → ~/.config/wezterm/wezterm.lua
~/.dotfiles/yazi/*.toml → ~/.config/yazi/*.toml
~/.dotfiles/nvim/* → ~/.config/nvim/*
~/.dotfiles/zsh/.zshrc → ~/.zshrc
- Industry standard: Used by most dotfiles repos
- Git tracks actual content: Not just symlink paths
- Simple to understand: No magic, just file links
- Works everywhere: No special tools required
Alternative approaches considered and rejected:
- Reverse symlinks (repo contains symlinks pointing to ~/.config): Git stores symlink paths, not file contents
- Git submodules: Unnecessary complexity for personal configs (5-6 repos to manage)
Three layers prevent accidental secret commits:
Scans staged files for API keys, tokens, and credentials before every commit.
# Manual scan
cd ~/.dotfiles && gitleaks detect --source .
# Run all pre-commit hooks
pre-commit run --all-filesBlocks 30+ sensitive file patterns:
*.env,.env.*(except templates)credentials.json,*.credentialsid_rsa,id_ed25519,*.key.aws/credentials,*.tfvars- And many more...
API keys go in ~/.secrets.env (gitignored), not in dotfiles:
# Copy template (done by install.sh)
cp ~/.dotfiles/secrets.env.template ~/.secrets.env
# Edit with your actual keys
vim ~/.secrets.env
# Source in your shell (add to ~/.zshrc)
[ -f ~/.secrets.env ] && source ~/.secrets.envDotfiles sync automatically every hour without manual commits.
- launchd agent triggers
auto-sync.shhourly - Brewfile is regenerated to capture new packages
- gitleaks scans for secrets before staging
- pre-commit hooks run before commit
- Changes are committed and pushed to GitHub
- macOS notification alerts you if anything fails
# Check sync status (add alias: dfs)
~/.dotfiles/scripts/sync-status.sh
# Output example:
# === Dotfiles Sync Status ===
# Status: OK
# Last sync: 2025-12-20 13:45:35
# Message: Synced ff7d92cThe .zshrc is managed by this repo and includes:
dfsalias for checking sync status- Auto-loading of
~/.secrets.env(gitignored) - Startup warning if sync is stale (>24 hours)
# Run sync manually
~/.dotfiles/scripts/auto-sync.sh
# Force sync (skip secret scan - not recommended)
~/.dotfiles/scripts/auto-sync.sh --force# Check if agent is running
launchctl list | grep dotfiles
# Reload agent
launchctl unload ~/Library/LaunchAgents/com.dotfiles.autosync.plist
launchctl load ~/Library/LaunchAgents/com.dotfiles.autosync.plist
# View sync logs
cat /tmp/dotfiles-sync.stdout.log
cat /tmp/dotfiles-sync.stderr.logBefore first install, a timestamped backup is created:
~/.dotfiles-backup-YYYYMMDD_HHMMSS/
├── aerospace/
├── ghostty/
├── wezterm/
├── yazi/
└── nvim/If something goes wrong:
# Restore all configs from backup
~/.dotfiles/rollback.sh
# This removes symlinks and restores original filesOnce everything works:
BACKUP_DIR=$(cat ~/.dotfiles-backup-location)
rm -rf "$BACKUP_DIR" ~/.dotfiles-backup-location~/.dotfiles/
├── aerospace/
│ └── aerospace.toml # Window manager config
├── ghostty/
│ ├── config # Terminal config
│ └── tmux-attach.sh # Tmux integration script
├── wezterm/
│ └── wezterm.lua # Terminal config (Lua)
├── yazi/
│ ├── yazi.toml # File manager config
│ ├── keymap.toml # Keybindings
│ ├── package.toml # Plugin packages
│ └── plugins/ # Installed plugins
├── nvim/
│ ├── init.lua # Neovim entry point
│ ├── lazy-lock.json # Plugin lockfile
│ ├── lazyvim.json # LazyVim config
│ ├── stylua.toml # Lua formatter config
│ └── lua/ # Lua config modules
├── zsh/
│ └── .zshrc # Shell config (aliases, functions, PATH)
├── scripts/
│ ├── auto-sync.sh # Automated sync with secret scanning
│ ├── sync-status.sh # Health monitoring
│ └── com.dotfiles.autosync.plist # launchd agent
├── Brewfile # Homebrew packages (auto-updated)
├── install.sh # New machine setup
├── rollback.sh # Restore from backup
├── secrets.env.template # API key template
├── .pre-commit-config.yaml # Pre-commit hook config
├── .gitleaks.toml # Secret scanner allowlist
└── .gitignore # Sensitive file patterns
To add a new application's config:
-
Move config to dotfiles:
mv ~/.config/newapp ~/.dotfiles/newapp
-
Create symlink:
ln -sf ~/.dotfiles/newapp ~/.config/newapp
-
Update install.sh to create the symlink on new machines
-
Commit:
cd ~/.dotfiles && git add -A && git commit -m "Add newapp config"
Or just wait for auto-sync.
# Check if launchd agent is loaded
launchctl list | grep dotfiles
# If not loaded, reload it
launchctl load ~/Library/LaunchAgents/com.dotfiles.autosync.plist# See what was detected
cd ~/.dotfiles && gitleaks detect --source . --verbose
# If false positive, add to .gitleaks.toml allowlist
# If real secret, remove it and add pattern to .gitignore# Re-run install script
cd ~/.dotfiles && ./install.sh~/.dotfiles/rollback.shMIT