Multi-Machine Setups¶
One of tidydots' greatest strengths is managing a single dotfiles repository that adapts to different machines. Whether you have a desktop, a laptop, a work machine, and a server, you can share one repo and let tidydots handle the differences.
How it works¶
tidydots provides three mechanisms for per-machine customization:
whenexpressions -- conditionally include or exclude entire applications- Template conditionals -- render different file content based on the current machine
- Path templating -- use machine-specific paths for config targets
All three use Go template syntax with access to the same context variables:
| Variable | Description | Example values |
|---|---|---|
.OS | Operating system | "linux", "windows" |
.Distro | Linux distribution ID | "arch", "ubuntu", "fedora" |
.Hostname | Machine hostname | "my-desktop", "work-laptop" |
.User | Current username | "alice", "root" |
.Env | Environment variables map | {{ index .Env "HOME" }} |
Conditional applications with when¶
The when field on an application controls whether it is included on the current machine. If the expression evaluates to anything other than "true", the application and all its entries are skipped.
Filter by hostname¶
Run an application only on a specific machine:
applications:
- name: "desktop-tweaks"
description: "Settings only for my desktop"
when: '{{ eq .Hostname "my-desktop" }}'
entries:
- name: "desktop-config"
backup: "./desktop"
targets:
linux: "~/.config/desktop-tweaks"
Filter by distribution¶
Restrict to a specific Linux distribution:
applications:
- name: "pacman-config"
description: "Pacman configuration"
when: '{{ eq .Distro "arch" }}'
entries:
- name: "pacman-conf"
backup: "./pacman"
targets:
linux: "/etc/pacman.conf"
package:
managers:
pacman: "pacman"
Filter by operating system¶
Include an application only on Linux or only on Windows:
applications:
- name: "linux-tools"
when: '{{ eq .OS "linux" }}'
entries:
- name: "tmux-config"
backup: "./tmux"
targets:
linux: "~/.config/tmux"
Combining conditions¶
Use and, or, and not to build complex expressions:
applications:
# Only on Ubuntu Linux
- name: "ubuntu-settings"
when: '{{ and (eq .OS "linux") (eq .Distro "ubuntu") }}'
entries:
- name: "ubuntu-config"
backup: "./ubuntu"
targets:
linux: "~/.config/ubuntu-settings"
# On any Linux except Arch
- name: "non-arch-settings"
when: '{{ and (eq .OS "linux") (ne .Distro "arch") }}'
entries:
- name: "generic-config"
backup: "./generic-linux"
targets:
linux: "~/.config/generic"
# Only on my personal machines
- name: "personal"
when: '{{ or (eq .Hostname "my-desktop") (eq .Hostname "my-laptop") }}'
entries:
- name: "personal-config"
backup: "./personal"
targets:
linux: "~/.config/personal"
Tip
All sprout template functions are available in when expressions, giving you access to string manipulation, regular expressions, and more.
Template conditionals in file content¶
For cases where you want the same config file but with different content on different machines, use template files. Any file with a .tmpl extension in your backup directory is rendered as a Go template during restore.
Basic example: hostname-specific settings¶
Create a file called kitty.conf.tmpl in your backup directory:
# Shared settings
font_family JetBrainsMono Nerd Font
font_size 11
background_opacity 0.9
{{ if eq .Hostname "my-desktop" -}}
# Desktop: larger monitor, enable fancy shader
custom_shader shaders/fancy.glsl
custom_shader_animation always
{{- end }}
{{ if eq .Hostname "my-laptop" -}}
# Laptop: smaller screen, boost font size
font_size 13
background_opacity 1.0
{{- end }}
When you run tidydots restore:
- On
my-desktop, the shader lines are included and the laptop block is omitted - On
my-laptop, the font size override is included and the desktop block is omitted - On any other machine, both blocks are omitted and you get the shared defaults
How template rendering works
kitty.conf.tmplis rendered tokitty.conf.tmpl.rendered(a sibling file in the backup directory)- A symlink is created from the target (e.g.,
~/.config/kitty/kitty.conf) to the.tmpl.renderedfile - Non-template files in the same directory get normal symlinks
Distro-specific content¶
# Package manager aliases
{{ if eq .Distro "arch" -}}
alias update="sudo pacman -Syu"
alias install="sudo pacman -S"
{{- else if eq .Distro "ubuntu" -}}
alias update="sudo apt update && sudo apt upgrade"
alias install="sudo apt install"
{{- else if eq .Distro "fedora" -}}
alias update="sudo dnf upgrade"
alias install="sudo dnf install"
{{- end }}
Environment variables¶
Access environment variables through the .Env map:
{{ if index .Env "WAYLAND_DISPLAY" -}}
# Wayland-specific settings
GDK_BACKEND=wayland
{{- else -}}
# X11 settings
GDK_BACKEND=x11
{{- end }}
Gitignore rendered files
Add these patterns to your dotfiles repo's .gitignore to avoid committing generated files:
Path templating¶
Config paths themselves can include template expressions. This lets you use different target directories on different machines.
Per-hostname config directories¶
applications:
- name: "nvim"
entries:
- name: "nvim-config"
backup: "./nvim"
targets:
linux: "~/.config/{{ .Hostname }}/nvim"
On a machine with hostname my-desktop, the symlink target becomes ~/.config/my-desktop/nvim. On my-laptop, it becomes ~/.config/my-laptop/nvim.
Per-user paths¶
applications:
- name: "shell-config"
entries:
- name: "profile"
backup: "./shell"
targets:
linux: "/home/{{ .User }}/.profile"
Note
Paths without {{ delimiters are treated as regular paths and go through the standard path expansion (tilde expansion, etc.). Template path evaluation is only triggered when {{ is detected.
Practical examples¶
Desktop vs. laptop setup¶
A common scenario: you want the same applications on both machines but with different display settings.
version: 3
backup_root: "."
applications:
# Shared on all machines
- name: "neovim"
entries:
- name: "nvim-config"
backup: "./nvim"
targets:
linux: "~/.config/nvim"
package:
managers:
pacman: "neovim"
apt: "neovim"
# Terminal emulator with per-machine rendering
- name: "kitty"
when: '{{ eq .OS "linux" }}'
entries:
- name: "kitty-config"
backup: "./kitty"
targets:
linux: "~/.config/kitty"
package:
managers:
pacman: "kitty"
apt: "kitty"
# Desktop-only: GPU-intensive tools
- name: "gpu-tools"
when: '{{ eq .Hostname "my-desktop" }}'
entries:
- name: "gpu-config"
backup: "./gpu"
targets:
linux: "~/.config/gpu-tools"
package:
managers:
pacman: "nvtop"
And in ./kitty/kitty.conf.tmpl:
font_family JetBrainsMono Nerd Font
{{ if eq .Hostname "my-desktop" -}}
font_size 11
background_opacity 0.85
{{- else -}}
font_size 13
background_opacity 1.0
{{- end }}
Cross-platform dotfiles (Linux + Windows)¶
version: 3
backup_root: "."
applications:
- name: "neovim"
entries:
- name: "nvim-config"
backup: "./nvim"
targets:
linux: "~/.config/nvim"
windows: "~/AppData/Local/nvim"
package:
managers:
pacman: "neovim"
apt: "neovim"
winget: "Neovim.Neovim"
scoop: "neovim"
- name: "git-config"
entries:
- name: "gitconfig"
files: [".gitconfig"]
backup: "./git"
targets:
linux: "~"
windows: "~"
# Linux-only
- name: "zsh"
when: '{{ eq .OS "linux" }}'
entries:
- name: "zshrc"
files: [".zshrc", ".zshenv"]
backup: "./zsh"
targets:
linux: "~"
package:
managers:
pacman: "zsh"
apt: "zsh"
# Windows-only
- name: "powershell"
when: '{{ eq .OS "windows" }}'
entries:
- name: "ps-profile"
backup: "./powershell"
targets:
windows: "~/Documents/PowerShell"
Work vs. personal machines¶
Use hostname-based when expressions to separate work and personal configurations:
applications:
# Shared everywhere
- name: "neovim"
entries:
- name: "nvim-config"
backup: "./nvim"
targets:
linux: "~/.config/nvim"
# Personal machines only
- name: "gaming"
when: '{{ or (eq .Hostname "my-desktop") (eq .Hostname "my-laptop") }}'
entries:
- name: "steam-config"
backup: "./steam"
targets:
linux: "~/.config/steam"
# Work machine only
- name: "work-tools"
when: '{{ eq .Hostname "work-laptop" }}'
entries:
- name: "work-config"
backup: "./work"
targets:
linux: "~/.config/work"
Debugging expressions¶
Use dry-run mode to verify which applications are included on the current machine without making any changes:
This shows all applications and entries that match the current machine's context, letting you confirm your when expressions are working as expected.
You can also override the OS for testing:
Next steps¶
- Templates -- full template syntax and 3-way merge details
- System Configs -- managing files that require sudo
- Configuration Overview -- complete configuration reference