GNU Stow: Managing Dotfiles Across a Homelab

Clone, stow, done. How I keep configs synchronized across four homelab machines with git and symlinks.

When you run a homelab with multiple machines, keeping configurations synchronized is tedious. Manual copying breaks, version tracking is a nightmare, and rebuilding a machine means hours of setup. GNU Stow, however, solves this with symlinks and git.

(I rebuilt a VM last week. Configs restored in under 30 seconds. That still feels weird.)

The Problem

I run four machines: main workstation, secondary machine, Proxmox VM, and development box. Each needs:

  • Shell configs (.bashrc, aliases)
  • Claude Code setups (commands, agents, knowledge base)
  • Git configs
  • SSH settings

Before Stow, I’d scp files around, forget which machine had the latest version, and break things. Rebuilding a machine meant manually copying configs for an hour.

The Solution

That’s why I use GNU Stow with a git repository.

The setup is organized by machine, not by application:

1
2
3
4
5
6
7
dotfiles/
├── claude-shared/       # Shared Claude configs across all machines
├── omarchy/            # Main workstation configs
├── black/              # Secondary machine configs
├── pve-vm-01/          # Proxmox VM configs
├── NC-6G90294/         # Development machine configs
└── etc-claude-code/    # System-level configs (/etc/claude-code/)

Each machine directory contains that machine’s specific configs. The claude-shared directory has Claude Code commands, agents, and knowledge base files that every machine uses.

How It Works

Stow creates symlinks from your home directory to files in the repo. When you edit a file at ~/.bashrc, you’re actually editing ~/dev/dotfiles/omarchy/.bashrc. Changes are immediately tracked in git.

Here’s the complete workflow for a new machine:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Install stow first
sudo pacman -S stow  # Arch
sudo apt install stow  # Debian/Ubuntu

# Clone the repo
git clone https://github.com/amlucas0xff/dotfiles.git ~/dev/dotfiles
cd ~/dev/dotfiles

# Install configs (replace 'omarchy' with your machine name)
stow -t ~ claude-shared omarchy

# Optional: system-level configs
sudo ./system-link.sh link etc-claude-code

That’s it. Your machine now has the right configs, all symlinked to the git repo.

Real Commands I Use

Adding a new machine:

1
2
3
4
mkdir ~/dev/dotfiles/new-machine
cp black/.bashrc new-machine/  # Start from existing machine
stow -t ~ claude-shared new-machine
git add . && git commit -m "Add new-machine" && git push

Updating configs:

1
2
3
vim ~/dev/dotfiles/omarchy/.bashrc  # Edit in place
cd ~/dev/dotfiles
git add . && git commit -m "Update bashrc" && git push

Pulling updates on another machine:

1
2
cd ~/dev/dotfiles && git pull
# Changes appear immediately (files are symlinked)

Handling conflicts:

1
2
3
# If files already exist, use --adopt to keep them
stow --adopt -t ~ omarchy
# This moves existing files into the repo and creates symlinks

Why This Is Useful

Machine rebuild is trivial: VM dies? Clone repo, run stow, back online in 30 seconds.

Configs sync automatically: Edit .bashrc on one machine, git push, git pull on another. The symlinks mean changes appear instantly.

Claude Code stays consistent: Custom commands, agents, and knowledge base are identical across all machines. My robot buddy behaves the same way everywhere.

Version history for free: See what your .bashrc looked like six months ago. Roll back when experiments break things.

Machine-specific + shared configs: Common Claude setup in claude-shared, machine-specific tweaks in per-machine directories.

Claude Code Specifics

The claude-shared/.claude/ directory contains:

1
2
3
4
5
6
7
.claude/
├── commands/           # Custom slash commands
├── agents/            # Specialized AI agents
├── knowledge/         # Documentation for context
├── hooks/             # Pre/post execution hooks
├── mcp/               # MCP server configs
└── settings.json      # Base settings

Each machine also has its own .claude/settings.local.json for machine-specific permissions and MCP tool configurations.

When I create a new Claude Code command on my workstation, it shows up on all machines after git pull. Same for agents, hooks, and knowledge base updates.

System-level configs (like /etc/claude-code/CLAUDE.md) use a helper script:

1
sudo ./system-link.sh link etc-claude-code

This creates absolute symlinks in /etc pointing back to the repo. Global Claude instructions stay synchronized without copying files around.

What Makes This Work

Three things:

  1. Symlinks: Edit files in place, git tracks changes automatically
  2. Machine-specific directories: Each machine gets its own configs without conflicts
  3. Shared directory: Common configs (especially Claude Code) live once, used everywhere

The homelab-focused structure means I can provision a new VM in minutes. Create directory, copy a .bashrc from an existing machine, stow, commit. Done.

If you run multiple Linux machines and find yourself copying configs around, try stow. It’s simpler than it looks.

uname -a: Human 5.4.0-anxiety #1 SMP Coffee-Deprived x86_64 GNU/Paranoid
Built with Hugo
Theme Stack designed by Jimmy