dotbot-go is a small command-line tool that helps you set up a computer from a dotfiles repository.
You write one config file that says:
- create these folders
- link these files into my home directory
- remove broken old symlinks
- run these setup commands
Then you run dotbot, preview the changes, and apply them.
This project is a Go implementation of the core Dotbot workflow. It keeps the familiar config style while shipping as a single binary.
Use dotbot-go when you keep configuration files in a Git repository and want to install them on a new machine or keep multiple machines in sync.
Common examples:
- link
~/.vimrctovimrcin your dotfiles repo - link
~/.tmux.conftotmux.conf - create folders like
~/.config/nvimor~/.local/bin - run setup commands such as
git submodule update --init --recursive
Install a prebuilt release with:
curl --proto '=https' --tlsv1.2 -sSf https://github.com/worxbend/dotbot-go/releases/latest/download/install.sh | shSet DOTBOT_VERSION to pin a specific release tag, or DOTBOT_INSTALL_DIR to install elsewhere. The script also adds the install directory to PATH in ~/.bashrc and ~/.zshrc if it's missing, and updates its entries when the install directory changes. On macOS, it also updates the active Bash login profile (~/.bash_profile, ~/.bash_login, or ~/.profile), creating ~/.bash_profile if none exists.
The script and release archives provide the dotbot command. Source builds below use the same name. Snap installations expose dotbot-go; replace dotbot with dotbot-go in the commands in these guides when using Snap. Restart your shell after script installation to load the updated PATH.
From this repository:
just buildThis creates:
bin/dotbot
If you do not have just, you can build with Go directly:
mkdir -p bin
go build -buildvcs=false -o bin/dotbot ./cmd/dotbot-goCreate install.conf.yaml in your dotfiles repository:
- defaults:
link:
create: true
relink: true
- clean:
- "~"
- create:
- "~/.config"
- "~/.local/bin"
- link:
~/.vimrc: vimrc
~/.tmux.conf: tmux.conf
~/.config/nvim:
path: nvim
- shell:
- [git submodule update --init --recursive, Installing submodules]In plain English, this means:
- use sensible defaults for links
- clean broken symlinks directly in your home directory that point into the base directory
- create a couple of folders
- link files from the repo into your home directory
- update Git submodules
Check that the config file can be parsed and only uses supported directive shapes:
./bin/dotbot validate -c install.conf.yamlValidation also reports how many operations would be planned. It does not create directories, links, or run shell commands.
Print the operations that the config expands into:
./bin/dotbot plan -c install.conf.yamlUse JSON when another tool needs to consume the plan:
./bin/dotbot plan -c install.conf.yaml --output jsonAlways run a dry run first:
./bin/dotbot -c install.conf.yaml --dry-runDry run prints proposed changes without changing your filesystem or running shell commands. For links with link.if, it reports the condition and destination, then skips both condition execution and filesystem preview. The outcome remains unknown until an actual run.
When the preview looks correct:
./bin/dotbot -c install.conf.yamlA typical dotfiles repository looks like this:
dotfiles/
├── install.conf.yaml
├── vimrc
├── tmux.conf
└── nvim/
Run dotbot from that repository:
cd ~/.dotfiles
dotbot -c install.conf.yamlOr point to the repository explicitly:
dotbot -d ~/.dotfiles -c ~/.dotfiles/install.conf.yamlThe -d option sets the base directory. Relative paths in your config, such as vimrc or nvim, are resolved from that directory.
dotbot-go supports these file formats:
| Format | Extensions | Best For |
|---|---|---|
| YAML | .yaml, .yml |
Most dotfiles users |
| JSON | .json |
Strict machine-generated config |
| JSON5 | .json5 |
JSON with comments and trailing commas |
| TOML | .toml |
Users who prefer TOML syntax |
Complete examples:
YAML, JSON, and JSON5 can use a top-level task list. TOML should put the task list under tasks.
More detail: Config Formats Guide
Directives are the actions in your config file. They run in the order you write them.
| Directive | What It Does |
|---|---|
defaults |
Sets default options for later directives |
clean |
Removes broken symlinks from selected directories |
create |
Creates directories |
link |
Creates symlinks or hardlinks |
shell |
Runs shell commands |
Shell commands and link.if conditions have a 10-minute timeout per command. The CLI and config do not expose a timeout override.
More detail: User Guide
Validate without applying changes:
dotbot validate -c install.conf.yamlThis reports whether the configuration is valid and how many operations would be planned.
Print planned operations:
dotbot plan -c install.conf.yamlPrint planned operations as JSON:
dotbot plan -c install.conf.yaml --output jsonPreview without changing files:
dotbot -c install.conf.yaml --dry-runRun only link tasks:
dotbot -c install.conf.yaml --only linkSkip shell commands:
dotbot -c install.conf.yaml --except shellShow more output:
dotbot -vv -c install.conf.yamlDisable color:
dotbot -c install.conf.yaml --no-colorForce color when piping output:
dotbot -c install.conf.yaml --force-colorStop after the first failed directive:
dotbot -c install.conf.yaml --exit-on-failuredotbot-go will not overwrite a normal file or directory by default.
If a destination already exists:
- use
backup: trueto rename the existing file before linking - use
force: trueonly when you intentionally want to remove the existing path - use
relink: trueto replace an existing symlink
Example:
- link:
~/.vimrc:
path: vimrc
backup: trueBackups are stored inside a new private directory named <destination>.dotbot-backup.<timestamp>-<random>. The original file or directory is moved into it under its original basename. Existing backups are preserved. If backup reservation or the move fails, the original destination remains in place. To restore a backup, remove the replacement link and move the saved file or directory back to its original destination.
If something looks wrong, start here:
dotbot -c install.conf.yaml --dry-run -vvCommon problems:
| Problem | What To Check |
|---|---|
unsupported config file format |
File extension must be one of the supported extensions |
configuration file must be a list of tasks |
YAML/JSON/JSON5 should be a list, TOML should use tasks |
| Link target does not exist | Make sure the source file exists in your dotfiles repo |
| Existing file blocks a link | Add backup: true, move the file manually, or intentionally use force: true |
| Shell command fails | Run with -vv to see command output |
More detail: Troubleshooting Guide
Useful commands:
just --list
just verify
just vulncheckWithout just:
gofmt -w .
go mod tidy
golangci-lint run ./...
go test ./...
go test -race ./...
go vet ./...
GOTOOLCHAIN=go1.26.4+auto go run golang.org/x/vuln/cmd/govulncheck@latest ./...
go build -buildvcs=false -o bin/dotbot ./cmd/dotbot-goGitHub Actions tests and builds the project on pushes and pull requests. To publish a release from the GitHub UI, run the Test, Build, and Release workflow manually, enter the version, and choose release or prerelease. The workflow creates the v tag, tests and builds that tag, and publishes the GitHub Release with Linux and macOS archives plus checksums.
Tags pushed directly still publish releases. When you push a tag that starts with v, for example v0.2.1, the workflow publishes a stable GitHub Release. Tags with prerelease suffixes, such as v0.2.1-rc.1, are published as prereleases.
Example:
git tag v0.2.1
git push origin v0.2.1