Skip to content

Syncing Configs Across Machines: Dotfiles at Scale

DodaTech Updated 2026-06-22 7 min read

In this tutorial, you'll learn to sync developer configurations across multiple machines including Git-based sync, cloud storage, dotfile managers, and handling secrets and environment-specific settings.

Why Config Sync Matters

A developer with three machines -- a work laptop, a personal laptop, and a desktop -- faces a problem: every machine needs the same shell config, editor setup, and development tools. Manually copying files is error-prone and outdates quickly. A config sync Strategy ensures every machine stays in sync, new machines are set up in minutes, and you never lose your carefully tuned environment.

By the end of this guide, you will implement a multi-machine config sync system that handles secrets, machine-specific settings, and automatic deployment.

The Config Sync Challenge

As the number of machines grows, so does the complexity of keeping configurations in sync.

flowchart TD
  A[Config Source of Truth] --> B[Work Laptop]
  A --> C[Personal Laptop]
  A --> D[Home Desktop]
  A --> E[CI Server]
  A --> F[Dev VM]
  B --> G[Machine-Specific Overrides]
  C --> G
  D --> G
  G --> H[Environment Variables]
  G --> I[OS-Specific Settings]
  G --> J[Work vs Personal Keys]

This is the most common and reliable approach. A single Git Repository contains all configurations, deployed via a script.

Repository Structure

dotfiles/
├── zsh/
│   ├── .zshrc
│   ├── .zprofile
│   ├── aliases.zsh
│   └── functions.zsh
├── nvim/
│   └── .config/
│       └── nvim/
│           ├── init.lua
│           ├── lsp.lua
│           └── plugins.lua
├── tmux/
│   └── .tmux.conf
├── git/
│   ├── .gitconfig
│   └── .gitignore_global
├── scripts/
│   ├── bootstrap.sh
│   ├── sync.sh
│   └── install-deps.sh
├── machine-specific/
│   ├── work/
│   │   ├── .env.local
│   │   └── .gitconfig-work
│   └── personal/
│       ├── .env.local
│       └── .gitconfig-personal
└── README.md

Bootstrap Script

#!/bin/bash
# bootstrap.sh — Setup a new machine

set -euo pipefail

DOTFILES_REPO="https://github.com/username/dotfiles.git"
DOTFILES_DIR="$HOME/.dotfiles"

echo "Bootstrapping dotfiles..."

# Clone repo
if [ ! -d "$DOTFILES_DIR" ]; then
  git clone "$DOTFILES_REPO" "$DOTFILES_DIR"
fi

cd "$DOTFILES_DIR"

# Detect machine type
if [ -n "${WORK_MACHINE:-}" ]; then
  MACHINE_TYPE="work"
elif [ -n "${PERSONAL_MACHINE:-}" ]; then
  MACHINE_TYPE="personal"
else
  echo "Machine type not set. Using 'personal' as default."
  MACHINE_TYPE="personal"
fi

# Create symlinks
echo "Creating symlinks..."

# Zsh
ln -sf "$DOTFILES_DIR/zsh/.zshrc" "$HOME/.zshrc"
ln -sf "$DOTFILES_DIR/zsh/.zprofile" "$HOME/.zprofile"
ln -sf "$DOTFILES_DIR/zsh/aliases.zsh" "$HOME/.aliases.zsh"

# Neovim
ln -sf "$DOTFILES_DIR/nvim/.config/nvim" "$HOME/.config/nvim"

# Tmux
ln -sf "$DOTFILES_DIR/tmux/.tmux.conf" "$HOME/.tmux.conf"

# Git
ln -sf "$DOTFILES_DIR/git/.gitconfig" "$HOME/.gitconfig"
ln -sf "$DOTFILES_DIR/git/.gitignore_global" "$HOME/.gitignore_global"

# Machine-specific configs
if [ -f "$DOTFILES_DIR/machine-specific/$MACHINE_TYPE/.env.local" ]; then
  ln -sf "$DOTFILES_DIR/machine-specific/$MACHINE_TYPE/.env.local" "$HOME/.env.local"
fi

if [ -f "$DOTFILES_DIR/machine-specific/$MACHINE_TYPE/.gitconfig-$MACHINE_TYPE" ]; then
  ln -sf "$DOTFILES_DIR/machine-specific/$MACHINE_TYPE/.gitconfig-$MACHINE_TYPE" "$HOME/.gitconfig-local"
fi

# Install dependencies
bash "$DOTFILES_DIR/scripts/install-deps.sh"

echo "Bootstrap complete! Restart your terminal."

Sync Script

#!/bin/bash
# sync.sh — Pull latest configs and apply

set -euo pipefail

DOTFILES_DIR="$HOME/.dotfiles"

echo "Syncing dotfiles..."

cd "$DOTFILES_DIR"
git pull origin main

# Re-run bootstrap to update symlinks
bash "$DOTFILES_DIR/bootstrap.sh"

echo "Sync complete!"

Strategy 2: Cloud Storage Sync

Use services like Dropbox, Google Drive, or iCloud to sync config files.

Dropbox Example

# Store configs in Dropbox
mv ~/.zshrc ~/Dropbox/Dotfiles/zsh/.zshrc
ln -s ~/Dropbox/Dotfiles/zsh/.zshrc ~/.zshrc

# Works out of the box on all machines with Dropbox

Pros and Cons

Factor Git-Based Cloud Storage
Version history Yes Limited
Conflict Resolution Excellent Poor
Secret handling Careful needed Same risk
Offline access Yes (after clone) Yes (if synced)
Multi-machine Excellent Excellent
Collaboration Yes (PRs) Manual

Handling Secrets Across Machines

Option 1: Environment-Specific Files

# ~/.zshrc — Source local env
if [ -f "$HOME/.env.local" ]; then
  source "$HOME/.env.local"
fi

# ~/.env.local (NOT in Git)
export GITHUB_TOKEN="ghp_abc123"
export SSH_KEY_PATH="$HOME/.ssh/id_ed25519"

Option 2: Encrypted Files with Git-Crypt

# Install git-crypt
brew install git-crypt

# Initialize
git-crypt init

# Mark files for encryption
echo "secrets/** filter=git-crypt diff=git-crypt" >}} .gitattributes

# Add file
echo "API_KEY=supersecret" > secrets/.env.local
git add secrets/.env.local
git commit -m "Add encrypted secrets"

# Unlock on another machine
git-crypt unlock /path/to/key

Option 3: Pass (Password Store)

# Install pass
brew install pass gpg

# Initialize store
pass init "your-gpg-key-id"

# Store secret
pass insert dev/github-token

# Retrieve
pass dev/github-token

Option 4: Environment Variables from CI/Secrets Manager

# For secrets managed outside dotfiles
# ~/.zshrc
if command -v vault &> /dev/null; then
  export GITHUB_TOKEN=$(vault read -field=token secret/github)
fi

Machine-Specific Configuration

Using Conditionals

# ~/.zshrc — Detect machine and apply config

# Machine-specific Git config
if [ -f "$HOME/.gitconfig-local" ]; then
  git config --file "$HOME/.gitconfig-local" --list
fi

# OS-specific settings
case "$OSTYPE" in
  darwin*)
    alias flushdns='dscacheutil -flushcache'
    alias brewup='brew update && brew upgrade && brew cleanup'
    ;;
  linux*)
    alias open='xdg-open'
    alias pbcopy='xclip -selection clipboard'
    alias pbpaste='xclip -selection clipboard -o'
    ;;
esac

# Host-specific settings
case "$HOSTNAME" in
  work-laptop)
    export JAVA_HOME="/usr/lib/jvm/java-17-openjdk"
    export DOCKER_HOST="unix:///var/run/docker.sock"
    ;;
  personal-mbp)
    export JAVA_HOME="/usr/local/opt/openjdk@17"
    export DOCKER_HOST="unix:///Users/$USER/.docker/run/docker.sock"
    ;;
esac

Automatic Sync Triggers

Git Post-Merge Hook

# .git/hooks/post-merge (in dotfiles repo)
#!/bin/bash
# Auto-reapply configs after pull
$HOME/.dotfiles/bootstrap.sh
echo "Configs updated after merge."

Cron Job

# Check for updates daily
0 9 * * * cd ~/.dotfiles && git pull --ff-only && bash Bootstrap.sh >> ~/.dotfiles/sync.log 2>&1

Team Config Sharing

# Shared team config in a company dotfiles repo
dotfiles/
├── team/
│   ├── .editorconfig
│   ├── .eslintrc.json
│   ├── .prettierrc
│   └── .gitconfig-team
├── personal/       # Individual overrides (gitignored)   └── .env.local

# ~/.zshrc
source ~/.dotfiles/team/team-aliases.zsh

Common Errors

Problem Cause Fix
Symlink conflict File already exists Backup and replace: mv ~/.zshrc ~/.zshrc.bak
Git pull fails with local changes Uncommitted modifications Use git stash before pulling, then git stash pop
Secret accidentally committed .env in Git tracking Use git filter-branch or bfg to remove; add to .gitignore
Machine-specific config not applied Wrong hostname detection Add a manual MACHINE_TYPE environment variable
Symlinks break after directory move Relative symlinks Use absolute paths in symlink targets

Practice Questions

A Git Repository with a Bootstrap script that creates symlinks.

2. How do you handle secrets in a public dotfiles Repository?

Use .env.local files (not tracked), git-crypt for encrypted files, or a password manager like pass.

3. How do you manage OS-specific settings in shared config files?

Use conditional blocks based on $OSTYPE or $HOSTNAME.

4. What is the purpose of a Bootstrap script?

It automates the setup of a new machine: cloning the repo, creating symlinks, and installing dependencies.

5. How do you auto-apply config changes after a git pull?

Use a post-merge Git hook or a cron job that runs git pull && Bootstrap.

Challenge

Create a multi-machine config sync system that: uses a Git Repository with a Bootstrap script, includes machine-specific overrides for work and personal environments, uses .env.local for secrets, has OS-specific settings, and includes an automatic sync trigger (post-merge hook or cron job). Deploy it on at least two machines.

Real-World Task

Audit all the configuration files across your machines. Identify which files differ between machines and which are identical. Create a single Git Repository with your shared configs. Add machine-specific overrides for work vs personal settings. Implement a Bootstrap script and deploy it on a new machine (or VM). Verify that the new machine is fully configured with the same environment in under 10 minutes.

Should I use symlinks or copy files?

Use symlinks. They make it clear that the file is managed by your dotfiles repo. When you update the repo, the symlinked file is always current.

How do I handle different shell versions on different machines?

Use conditional blocks with version checks: if [[ $ZSH_VERSION == 5.9* ]]; then ....

Can I sync dotfiles without a public Git Repository?

Yes. Use a private GitHub Repository, a self-hosted Git server, or cloud storage with symlinks (Dropbox, Google Drive).

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro