dots
dots is a Go command-line tool for managing dotfiles across machines and contexts. It borrows Homebrew’s organizational model — where a repository is just a list of packages — and chezmoi’s ability to bootstrap itself onto a fresh system. The result is a dotfile manager that handles multiple independent sets of configuration (public, personal, work) without them stepping on each other.
Why another dotfile manager
Most dotfile approaches boil down to a single Git repo with a script that symlinks everything into place. That works fine until you have configuration that can’t be public, or you need different setups for different machines. Stow gets you halfway there, but it doesn’t handle multiple repos, platform differences, or the bootstrapping problem — getting the tool itself installed on a new machine.
dots treats each Git repository as a tap (borrowed from Homebrew’s terminology). A tap can contain multiple
packages, each with its own Dotfile.yaml manifest declaring what files go where and what hooks to run. You can
maintain a public tap for your shell and editor config, a private tap for credentials and SSH keys, and a work tap for
company-specific tooling — all managed by the same tool.
Core concepts
Taps are named pointers to Git repositories. Any Git repo can be a tap — public or private, on GitHub or anywhere else. Each tap holds one or more packages.
Packages are directories within a tap containing a Dotfile.yaml manifest and the configuration files to install.
The manifest declares links (where files go), hooks (scripts to run at install/upgrade/remove), and platform-specific
overrides.
Overlays layer on top of base packages, merging or replacing specific files. A work overlay on a personal zsh package can append company-specific aliases without forking the base config. Merged files get visible marker comments so you can always tell where each section came from.
Profiles are ordered collections of packages and overlays applied together. A work profile can extend a personal
profile, adding work-specific packages on top of the personal baseline.
Cross-platform path resolution
Dotfiles land in different locations on different operating systems. dots solves this with path aliases — @config
resolves to ~/.config on Linux/macOS and %APPDATA% on Windows, @home resolves to $HOME or %USERPROFILE%, and
so on. A link target of @config/nvim/init.lua does the right thing on every platform without requiring
platform-specific sections in the manifest.
When platform differences go beyond paths, the manifest supports OS and OS-arch overrides that cascade from general to specific:
base settings → OS-only overrides → OS-arch-specific overridesSelf-bootstrapping
dots manages its own configuration — the dots config is itself a package in a tap. On a fresh machine:
dots init --from <git-url> --path <directory>This clones the tap, applies the dots package (installing its own config), and establishes the initial state. From there, applying a profile pulls in everything else.
Link strategies
dots supports three ways to place files: symlink (default, changes propagate instantly), copy (manual sync via
dots sync, for restricted filesystems), and hardlink (instant updates within the same filesystem). The strategy
cascades from package-level settings down to global defaults, and on Windows the system auto-detects symlink capability
and falls back to copy if needed.
Design constraints
The tool deliberately avoids template languages, registries, and magic. Configuration is plain YAML. Taps are just Git repos. Every operation is reversible through backups and lockfile tracking. The goal is a system where you can always read the manifest and understand exactly what will happen.