Skip to content

Repository files navigation

dots

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.

Install

One-liner (Linux / macOS):

curl -fsSL https://raw.githubusercontent.com/pkuehne/dots/refs/heads/main/install.sh | sh

From source (requires Go 1.21+):

git clone https://github.com/pkuehne/dots.git
cd dots
go build -o ~/.local/bin/dots ./cmd/dots

Upgrading: 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

Quick start

# 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 apply

That's it. No config file is required — dots discovers files under files/ and symlinks them into ~.

How it works

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)

File discovery

~/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)

Configuration

All configuration lives in dots.toml. Every section is optional.

Environment variables

[env]
EDITOR = "nvim"
LANG = "en_US.UTF-8"

[[env.when]]
platform = "darwin"
env.HOMEBREW_NO_ANALYTICS = "1"

PATH management

[shell]
managed = true
path = ["~/.cargo/bin", "~/.local/bin"]

Tool installation

[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 config

[git]
managed = true
name = "Your Name"
email = "you@example.com"
editor = "nvim"

SSH config

[ssh]
managed = true

[[ssh.host]]
host = "dev"
hostname = "dev.example.com"
user = "deploy"
identity_file = "~/.ssh/id_ed25519"

Repositories

[[repo]]
name = "private-config"
url = "git@github.com:you/private-config.git"
dest = "~/code/private-config"

Profiles

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

[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.

Commands

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.

Platforms

  • Linux — full support, apt + github install methods
  • macOS — full support, brew + github install methods
  • Termux — full support, uses pkg instead of apt, no sudo
  • WSL — detected as Linux, use profiles for WSL-specific config

Development

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 .

Roadmap

See ROADMAP.md for planned features and improvements.

License

MIT

About

A comprehensive dotfile, tools and shell manager

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages