Simple, portable, framework-free dotfiles for macOS, Linux and Dev Containers.
Configuration is treated as data (plain files under version control) and
automation as code (Bash modules under lib/). Files are exposed to the
system through symlinks — nothing inside $HOME is edited directly.
git clone <this-repo> ~/.dotfiles
cd ~/.dotfiles
./install.shRunning ./install.sh with no arguments performs a full, idempotent bootstrap.
Re-running it is always safe.
Point the dotfiles feature at this repository with ./install.sh as the
install command. No absolute paths are used, so it works unchanged.
./install.sh # full installation
./install.sh packages # install required system packages
./install.sh symlinks # link home/ -> ~/ and config/ -> ~/.config/
./install.sh secrets # prepare ~/.config/dotfiles/secrets.zsh
./install.sh ohmyzsh # install Oh My Zsh (keeps existing .zshrc)
./install.sh doctor # validate the environment
./install.sh help # usagedotfiles/
├── install.sh # single entry point (Bash)
├── lib/ # automation modules — functions only, no side effects
│ ├── common.sh # logging, colors, helpers
│ ├── os.sh # OS / package-manager detection
│ ├── packages.sh # package installation
│ ├── symlinks.sh # generic symlink creation
│ ├── ohmyzsh.sh # Oh My Zsh installation
│ ├── secrets.sh # local secrets management
│ └── doctor.sh # environment diagnostics
├── home/ # linked into $HOME/ (e.g. .zshrc, .gitconfig)
├── config/ # linked into $HOME/.config/ (e.g. nvim/ — NvChad)
├── scripts/ # standalone helper scripts
├── secrets.example.zsh # documents expected secret variables
├── README.md
└── .gitignore
- Symlinks are generic. Every top-level entry of
home/is linked into$HOME/, and every entry ofconfig/into$HOME/.config/. Adding a new file to the repo is all it takes — there are no hard-coded lists. Existing real files are backed up (*.backup.<timestamp>) before being replaced. - Only the entry point knows paths.
install.shcomputesDOTFILES_ROOT,DOTFILES_LIB,DOTFILES_HOMEandDOTFILES_CONFIGand loads the modules. Modules never source each other and never recompute the root. - Secrets are never versioned. They live at
~/.config/dotfiles/secrets.zshand are sourced by.zshrconly when present. The repo ships onlysecrets.example.zsh. Machine-specific git settings go in~/.config/dotfiles/gitconfig.local. - Neovim / NvChad is versioned config, not automation. The NvChad starter
was installed once (per the official docs),
its
.gitremoved, and the generated files committed underconfig/nvim.create_symlinkslinks it to~/.config/nvim; on firstnvimlaunch, lazy.nvim installs the plugins. Update with:Lazy syncfrom inside Neovim.
- Scripts start with
#!/usr/bin/env bashandset -Eeuo pipefail. - Public functions:
install_packages,create_symlinks,install_ohmyzsh,detect_os, ... Private helpers are prefixed with_. - All code aims to be idempotent, single-responsibility and ShellCheck-clean.