Dotfile management, tool installation, and shell environment generation. Works on Linux, macOS, and Termux with zero mandatory dependencies. Distributed as a single static binary — no runtime required.
One-liner (Linux / macOS):
curl -fsSL https://raw.githubusercontent.com/pkuehne/dots/refs/heads/main/install.sh | shFrom source (requires Go 1.21+):
git clone https://github.com/pkuehne/dots.git
cd dots
go build -o ~/.local/bin/dots ./cmd/dotsUpgrading: once installed, dots manages itself — no need to re-run the installer.
dots upgrade # replace the running binary with the latest release
dots upgrade --check # just report whether a newer version exists# Scaffold a new dotfiles repo
dots init ~/dotfiles
# See every config option, fully commented, to build out dots.toml
dots sample > ~/dotfiles/dots.toml
# Copy existing dotfiles in
cp ~/.zshrc ~/dotfiles/files/
cp ~/.gitconfig ~/dotfiles/files/
# Or scan for common dotfiles automatically
cd ~/dotfiles && dots migrate --write
# Preview what will happen
dots apply --dry-run
# Deploy symlinks
dots applyThat's it. No config file is required — dots discovers files under files/
and symlinks them into ~.
Dots uses a layered configuration model:
| Level | What you get |
|---|---|
| Zero config | Drop files into files/, run dots apply, get symlinks into ~ |
| dots.toml | Override destinations, add platform guards, set env vars, configure PATH |
| Managed subsystems | Let dots own your shell init, git config, or SSH config |
| Presets | Opinionated bundles (ejectable to plain files) |
~/dotfiles/
files/ # -> symlinked to ~/
.zshrc # -> ~/.zshrc
.config/
nvim/
init.lua # -> ~/.config/nvim/init.lua
files.d/
linux/ # -> only deployed on Linux
.Xresources
darwin/ # -> only deployed on macOS
.Brewfile
shell/ # -> shell snippets (if shell.managed)
dots.toml # -> configuration (optional)
Files are handled automatically by suffix:
.age— decrypted with age, written as a regular file- Everything else — symlinked verbatim (no suffix is special-cased beyond
.age)
All configuration lives in dots.toml. Every section is optional.
[env]
EDITOR = "nvim"
LANG = "en_US.UTF-8"
[[env.when]]
platform = "darwin"
env.HOMEBREW_NO_ANALYTICS = "1"[shell]
managed = true
path = ["~/.cargo/bin", "~/.local/bin"][tools]
bin_dir = "~/.local/bin"
[[tool]]
name = "rg"
check = "rg --version"
tags = ["core"]
[[tool.install]]
method = "apt"
package = "ripgrep"
[[tool.install]]
method = "brew"
package = "ripgrep"
[[tool.install]]
method = "github"
repo = "BurntSushi/ripgrep"
asset = "ripgrep-{version}-{arch}-unknown-linux-musl.tar.gz"
bin = ["rg"]Install methods: apt, brew, pkg, cargo, go, pip, pipx,
npm, github, script, manual.
[git]
managed = true
name = "Your Name"
email = "you@example.com"
editor = "nvim"[ssh]
managed = true
[[ssh.host]]
host = "dev"
hostname = "dev.example.com"
user = "deploy"
identity_file = "~/.ssh/id_ed25519"[[repo]]
name = "private-config"
url = "git@github.com:you/private-config.git"
dest = "~/code/private-config"Override any config per platform, hostname, or manual flag:
[profiles.work-laptop]
env.HTTP_PROXY = "http://proxy.corp:8080"
git.email = "you@work.com"
[profiles.linux]
shell.path = ["/snap/bin"][secrets]
identity = "~/.config/dots/key.txt"Encrypt files with dots encrypt, decrypt with dots decrypt.
Files ending in .age under files/ are decrypted automatically during apply.
| Command | Description |
|---|---|
dots init [dir] |
Scaffold a new dots repository |
dots sample |
Print a fully-commented example dots.toml (every option, documented) |
dots upgrade |
Upgrade the dots binary to the latest release |
dots upgrade --check |
Report whether a newer dots release is available |
dots apply |
Deploy files, generate configs, clone repos |
dots apply --dry-run |
Preview without making changes |
dots apply --summary |
Deploy, printing only the summary line (no per-file status) |
dots status |
Show deployment state |
dots diff [file] |
Show diffs between source and deployed |
dots edit <file> |
Open a managed file in your editor |
dots add <path> |
Adopt an existing file into the repo |
dots list |
List managed files |
dots doctor |
System health check |
dots migrate |
Scan for unmanaged dotfiles |
dots tools check |
Check which tools are installed |
dots tools install |
Install missing tools |
dots tools list |
List configured tools |
dots shell show |
Print generated shell snippets |
dots git init |
Enable managed git config |
dots git show |
Print managed gitconfig |
dots ssh init |
Enable managed SSH config |
dots ssh show |
Print managed SSH config |
dots repos clone |
Clone missing repositories |
dots repos status |
Show repository states |
dots env show |
Print generated env snippet |
dots encrypt <file> |
Encrypt a file with age |
dots decrypt <file> |
Decrypt an .age file |
All commands are idempotent. Running twice produces the same result.
- Linux — full support,
apt+githubinstall methods - macOS — full support,
brew+githubinstall methods - Termux — full support, uses
pkginstead ofapt, no sudo - WSL — detected as Linux, use profiles for WSL-specific config
git clone https://github.com/pkuehne/dots.git
cd dots
just build # or: go build -o bin/dots ./cmd/dots
just test # or: go test ./...
just fmt # or: gofmt -w .See ROADMAP.md for planned features and improvements.
MIT