Eintrag

GNU Stow: Automatisiertes Symlink-Management für Dotfiles

GNU Stow: Automatisiertes Symlink-Management für Dotfiles

Jeder, der schon einmal versucht hat, Dotfiles zwischen mehreren Systemen zu synchronisieren, kennt das Problem: Dutzende ln -sf-Befehle, die man bei jeder Neuinstallation von Hand ausführen muss. GNU Stow löst dieses Problem elegant und automatisiert das Symlink-Management mit einem einzigen Befehl.

Dieser Artikel baut auf dem Dotfiles-Grundlagen-Post auf. Falls du noch nicht mit Dotfiles und Symlinks vertraut bist, lies dort zuerst nach.


Was ist GNU Stow?

GNU Stow ist ein Symlink-Farm-Manager - ein Tool, das automatisch symbolische Links von einem Quellverzeichnis in ein Zielverzeichnis erstellt. Ursprünglich wurde es für die Verwaltung von Software in /usr/local entwickelt, hat sich aber als perfektes Werkzeug für Dotfiles-Management etabliert.

Manuell Mit GNU Stow
ln -sf ~/dotfiles/zsh/.zshrc ~/.zshrc stow zsh
ln -sf ~/dotfiles/git/.gitconfig ~/.gitconfig stow git
ln -sf ~/dotfiles/tmux/.tmux.conf ~/.tmux.conf stow tmux
20+ Befehle für ein komplettes Setup Ein Befehl: stow */

Vorteile von Stow:

  • Ein Befehl statt vieler ln -sf-Aufrufe
  • Automatische Erkennung der richtigen Pfade
  • Sauberes Entfernen von Symlinks mit stow -D
  • Dry-Run-Modus zum Testen
  • Konflikt-Erkennung verhindert Datenverlust
  • Keine Dependencies - reines Perl, auf fast jedem System verfügbar

Installation

GNU Stow ist in den Paketquellen aller gängigen Distributionen enthalten:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Debian/Ubuntu
sudo apt install stow

# Fedora
sudo dnf install stow

# Arch Linux
sudo pacman -S stow

# openSUSE
sudo zypper install stow

# macOS (Homebrew)
brew install stow

Version prüfen:

1
2
stow --version
# stow (GNU Stow) version 2.3.1

Das Stow-Grundkonzept

Stow arbeitet mit drei zentralen Konzepten:

Stow Konzept Die drei Kernkonzepte von GNU Stow

1. Stow Directory

Das Stow Directory ist der Ordner, in dem deine Pakete liegen. Standardmäßig ist das das aktuelle Verzeichnis.

1
2
# Explizit angeben mit -d oder --dir
stow -d ~/dotfiles zsh

2. Package

Ein Package ist ein Unterordner im Stow Directory. Jedes Paket enthält die Dateien und Ordner, die verlinkt werden sollen - in der exakt gleichen Struktur wie im Zielverzeichnis.

3. Target Directory

Das Target Directory ist das Ziel für die Symlinks. Standardmäßig ist das das Elternverzeichnis des Stow Directory.

1
2
# Explizit angeben mit -t oder --target
stow -t ~ zsh

Wichtig: Wenn dein Dotfiles-Ordner in ~/dotfiles/ liegt, ist das Target automatisch ~ (Home). Das ist genau das, was wir für Dotfiles wollen!


Die richtige Verzeichnisstruktur

Der Schlüssel zu erfolgreichem Stow-Einsatz ist die korrekte Verzeichnisstruktur. Die Paketstruktur muss die Zielstruktur exakt spiegeln.

Verzeichnisstruktur Die Paketstruktur spiegelt die Zielstruktur

Einfache Dotfiles im Home-Verzeichnis

Für Dateien, die direkt in ~ landen sollen:

1
2
3
4
5
6
7
8
~/dotfiles/
├── zsh/
│   ├── .zshrc          # wird zu ~/.zshrc
│   └── .zprofile       # wird zu ~/.zprofile
├── git/
│   └── .gitconfig      # wird zu ~/.gitconfig
└── tmux/
    └── .tmux.conf      # wird zu ~/.tmux.conf

Verschachtelte Strukturen (.config/)

Für Dateien in ~/.config/ musst du die komplette Pfadstruktur im Paket abbilden:

1
2
3
4
5
6
7
~/dotfiles/
└── nvim/
    └── .config/        # Dieser Pfad wird gespiegelt!
        └── nvim/
            ├── init.lua
            └── lua/
                └── plugins.lua

Nach stow nvim existiert:

  • ~/.config/nvim/ → Symlink zu dotfiles/nvim/.config/nvim/

Häufiger Fehler: Die .config/-Ebene vergessen! Ohne sie würde Stow versuchen, nvim/ direkt in ~ anzulegen.

Komplettes Beispiel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
~/dotfiles/
├── zsh/
│   ├── .zshrc
│   └── .zprofile
├── git/
│   └── .gitconfig
├── tmux/
│   └── .tmux.conf
├── nvim/
│   └── .config/
│       └── nvim/
│           ├── init.lua
│           └── lua/
├── alacritty/
│   └── .config/
│       └── alacritty/
│           └── alacritty.toml
└── ssh/
    └── .ssh/
        └── config

Basis-Kommandos

1
2
3
4
5
6
7
8
9
10
cd ~/dotfiles

# Einzelnes Paket
stow zsh

# Mehrere Pakete
stow zsh git tmux

# Alle Pakete (Globbing)
stow */
1
2
3
4
5
# Mit -D oder --delete
stow -D zsh

# Mehrere Pakete
stow -D zsh git tmux

Restow (Delete + Stow)

Nützlich nach Änderungen an der Paketstruktur:

1
2
# Mit -R oder --restow
stow -R zsh

Dry-Run (Simulation)

Zeigt, was passieren würde, ohne Änderungen vorzunehmen:

1
2
3
# Mit -n oder --no
stow -n zsh
# LINK: .zshrc => dotfiles/zsh/.zshrc

Verbose Output

Für mehr Details:

1
2
3
4
5
6
# Mit -v (mehrfach für mehr Details: -vv, -vvv)
stow -v zsh
# LINK: .zshrc => dotfiles/zsh/.zshrc

stow -vv zsh
# Detaillierter Output mit Pfadauflösung

Kommando-Übersicht

Kommando Kurzform Funktion
stow <pkg> - Symlinks erstellen
stow --delete <pkg> -D Symlinks löschen
stow --restow <pkg> -R Delete + Stow
stow --no <pkg> -n Dry-Run (nur anzeigen)
stow --verbose <pkg> -v Verbose Output
stow --dir=DIR -d DIR Stow Directory angeben
stow --target=DIR -t DIR Target Directory angeben

Praxis-Beispiele

Beispiel 1: Einfache Dotfiles

Ein minimales Setup für zsh, git und tmux:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Struktur erstellen
mkdir -p ~/dotfiles/{zsh,git,tmux}

# Bestehende Configs kopieren
cp ~/.zshrc ~/dotfiles/zsh/
cp ~/.gitconfig ~/dotfiles/git/
cp ~/.tmux.conf ~/dotfiles/tmux/

# Alte Dateien entfernen (Stow braucht freie Ziele)
rm ~/.zshrc ~/.gitconfig ~/.tmux.conf

# Symlinks erstellen
cd ~/dotfiles
stow zsh git tmux

# Prüfen
ls -la ~ | grep -E "zshrc|gitconfig|tmux"
# lrwxrwxrwx .gitconfig -> dotfiles/git/.gitconfig
# lrwxrwxrwx .tmux.conf -> dotfiles/tmux/.tmux.conf
# lrwxrwxrwx .zshrc -> dotfiles/zsh/.zshrc

Beispiel 2: Neovim mit .config/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Struktur mit verschachteltem .config
mkdir -p ~/dotfiles/nvim/.config/nvim

# Bestehende Config verschieben
mv ~/.config/nvim/* ~/dotfiles/nvim/.config/nvim/

# Altes Verzeichnis entfernen
rmdir ~/.config/nvim

# Stow ausführen
cd ~/dotfiles
stow nvim

# Prüfen
ls -la ~/.config/
# lrwxrwxrwx nvim -> ../dotfiles/nvim/.config/nvim

Beispiel 3: Alle Pakete auf einmal

1
2
3
4
5
6
7
8
9
10
11
12
13
cd ~/dotfiles

# Dry-Run für alle Pakete
stow -n */

# Wenn alles gut aussieht: ausführen
stow */

# Output:
# LINK: .zshrc => dotfiles/zsh/.zshrc
# LINK: .gitconfig => dotfiles/git/.gitconfig
# LINK: .tmux.conf => dotfiles/tmux/.tmux.conf
# LINK: .config/nvim => ../dotfiles/nvim/.config/nvim

Beispiel 4: Setup von Grund auf

Komplettes Dotfiles-Repository für ein neues System:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. Repository klonen
git clone [email protected]:user/dotfiles.git ~/dotfiles

# 2. Ins Verzeichnis wechseln
cd ~/dotfiles

# 3. Alle Pakete stow'en (Dry-Run zuerst)
stow -n */

# 4. Bei Konflikten: existierende Dateien sichern
# (siehe Troubleshooting)

# 5. Stow ausführen
stow */

# 6. Shell neu laden
exec zsh

Fortgeschrittene Features

–target: Alternatives Zielverzeichnis

Standardmäßig ist das Target das Elternverzeichnis. Mit -t kannst du ein anderes Ziel angeben:

1
2
3
4
5
# Symlinks in /etc erstellen (benötigt sudo)
sudo stow -t /etc myetc

# Von überall aus arbeiten
stow -d ~/dotfiles -t ~ zsh

–dir: Alternatives Stow Directory

1
2
3
4
5
# Stow Directory explizit angeben
stow -d /path/to/dotfiles zsh

# Kombination mit --target
stow -d ~/dotfiles -t ~ zsh git tmux

–ignore: Dateien ausschließen

Mit --ignore kannst du Dateien vom Stowing ausschließen:

1
2
3
4
5
# README und LICENSE ignorieren
stow --ignore='README.*' --ignore='LICENSE' zsh

# Backup-Dateien ignorieren
stow --ignore='.*\.bak' --ignore='.*~' zsh

.stow-local-ignore: Permanente Ignore-Regeln

Erstelle eine .stow-local-ignore-Datei im Paket für permanente Ausschlüsse:

1
2
3
4
5
# ~/dotfiles/zsh/.stow-local-ignore
README.md
LICENSE
\.git
.*\.bak

Die Datei verwendet Perl-Regex-Syntax, keine Glob-Patterns!

–adopt: Existierende Dateien übernehmen

--adopt ist mächtig aber gefährlich: Es verschiebt existierende Dateien ins Paket und erstellt dann Symlinks.

1
2
# Vorsicht: Überschreibt Dateien im Paket!
stow --adopt zsh

Workflow mit –adopt:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. Dry-Run
stow -n --adopt zsh
# LINK: .zshrc => dotfiles/zsh/.zshrc (ADOPTED)

# 2. Ausführen
stow --adopt zsh

# 3. Änderungen prüfen
cd ~/dotfiles
git diff

# 4. Bei Bedarf zurücksetzen
git checkout -- zsh/.zshrc

Warnung: --adopt überschreibt Dateien im Paket ohne Nachfrage! Immer vorher git status prüfen und ggf. committen.

Standardmäßig erstellt Stow Verzeichnis-Symlinks wenn möglich (genannt “Tree Folding”). Mit --no-folding werden stattdessen einzelne Datei-Symlinks erstellt:

1
2
3
4
5
6
7
8
9
# Standard (Tree Folding)
stow nvim
# ~/.config/nvim -> ../dotfiles/nvim/.config/nvim (ein Symlink)

# Mit --no-folding
stow --no-folding nvim
# ~/.config/nvim/init.lua -> ../../dotfiles/nvim/.config/nvim/init.lua
# ~/.config/nvim/lua/plugins.lua -> ...
# (mehrere Symlinks)

Wann –no-folding verwenden?

  • Wenn du manche Dateien in einem Verzeichnis nicht verlinken willst
  • Bei Konflikten mit anderen Paketen
  • Wenn andere Programme Dateien im gleichen Verzeichnis erstellen sollen

–override und –defer: Konflikt-Handling

Diese Optionen steuern, wie Stow mit Konflikten umgeht:

1
2
3
4
5
# --override: Bestimmte Pfade dürfen überschrieben werden
stow --override='\.config/nvim/.*' nvim

# --defer: Bestimmte Pfade werden übersprungen wenn belegt
stow --defer='\.bashrc' bash

Anwendungsfall: Wenn mehrere Pakete die gleiche Datei haben könnten:

1
2
3
4
5
# base-Paket hat .bashrc
stow base

# bash-extras-Paket hätte auch .bashrc, aber defer
stow --defer='\.bashrc' bash-extras

Troubleshooting

Problem: “existing target is not owned by stow”

Ursache: Eine reguläre Datei existiert bereits am Zielort.

1
2
3
4
$ stow zsh
WARNING! stowing zsh would cause conflicts:
  * existing target is neither a link nor a directory: .zshrc
All operations aborted.

Lösungen:

1
2
3
4
5
6
7
8
9
# Option 1: Datei manuell sichern und löschen
mv ~/.zshrc ~/.zshrc.backup
stow zsh

# Option 2: --adopt verwenden (überschreibt Paket-Datei!)
stow --adopt zsh

# Option 3: Dry-Run für Details
stow -n -v zsh

Problem: “cannot stow over existing directory”

Ursache: Ein Verzeichnis existiert bereits und ist kein Symlink.

1
2
3
$ stow nvim
WARNING! stowing nvim would cause conflicts:
  * existing target is a directory: .config/nvim

Lösungen:

1
2
3
4
5
6
7
8
9
10
# Option 1: Verzeichnis sichern und entfernen
mv ~/.config/nvim ~/.config/nvim.backup
stow nvim

# Option 2: --adopt verwenden
stow --adopt nvim

# Option 3: --no-folding für Datei-level Symlinks
rm -rf ~/.config/nvim  # Vorsicht!
stow --no-folding nvim

Ursache: Paket wurde verschoben oder umbenannt.

1
2
$ ls -la ~/.zshrc
lrwxrwxrwx .zshrc -> dotfiles/zsh/.zshrc (broken)

Lösung:

1
2
3
# Restow mit korrekten Pfaden
cd ~/dotfiles
stow -R zsh

Debugging-Tipps

1
2
3
4
5
6
7
8
9
10
11
# Maximaler Verbose-Output
stow -vvv zsh

# Dry-Run mit Details
stow -n -vv zsh

# Symlink-Ziel prüfen
readlink ~/.zshrc

# Alle Symlinks im Home finden
find ~ -maxdepth 1 -type l -ls 2>/dev/null

Best Practices

1. Naming-Konventionen

1
2
3
4
5
6
7
8
9
# Gut: lowercase, beschreibend
zsh/
git/
nvim/
tmux/

# Vermeiden: Groß-/Kleinschreibung gemischt
ZSH/
NeoVim/

2. README im Dotfiles-Repo

Dokumentiere, welche Pakete verfügbar sind:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Meine Dotfiles

## Pakete

| Paket | Beschreibung | Dependencies |
|-------|--------------|--------------|
| zsh | Zsh-Konfiguration mit Oh-My-Zsh | zsh, oh-my-zsh |
| git | Git-Konfiguration | git |
| nvim | Neovim mit LazyVim | neovim >= 0.9 |
| tmux | tmux-Konfiguration | tmux |

## Installation

\`\`\`bash
git clone <repo> ~/dotfiles
cd ~/dotfiles
stow */
\`\`\`

3. .stowrc für Defaults

Erstelle eine .stowrc-Datei im Stow Directory für Standard-Optionen:

1
2
3
4
5
# ~/dotfiles/.stowrc
--target=/home/user
--ignore='README.*'
--ignore='LICENSE'
--ignore='\.git.*'

4. Git-Integration

1
2
3
4
5
# ~/dotfiles/.gitignore
*.backup
*.bak
*~
.DS_Store

5. Maschinenspezifische Konfiguration

Nutze Host-spezifische Pakete:

1
2
3
4
5
~/dotfiles/
├── zsh/              # Basis für alle Maschinen
├── zsh-work/         # Nur auf Arbeits-PC
├── zsh-server/       # Nur auf Servern
└── zsh-laptop/       # Nur auf Laptop
1
2
3
4
5
# Auf dem Arbeits-PC
stow zsh zsh-work

# Auf dem Server
stow zsh zsh-server

6. Install-Script

Automatisiere das Setup:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# ~/dotfiles/install.sh

DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$DOTFILES_DIR"

# Alle Standard-Pakete
PACKAGES="zsh git tmux nvim"

echo "Installing dotfiles from $DOTFILES_DIR"

for pkg in $PACKAGES; do
    if [ -d "$pkg" ]; then
        echo "Stowing $pkg..."
        stow -v "$pkg"
    fi
done

echo "Done!"

Vergleich mit Alternativen

Tool-Vergleich Dotfiles-Tools im Vergleich

GNU Stow

Stärken:

  • Keine Dependencies (reines Perl)
  • Minimalistisch und schnell zu lernen
  • KISS-Prinzip: macht eine Sache gut
  • Perfekt für einfache Setups

Schwächen:

  • Keine Templates
  • Kein Secret-Management
  • Multi-Machine nur über separate Pakete

Ideal für: Entwickler, die ein einfaches, robustes Tool wollen.

chezmoi

Stärken:

  • Mächtiges Template-System (Go Templates)
  • Secret-Management (1Password, Bitwarden, etc.)
  • Multi-Machine mit Conditionals
  • Diff-Ansicht vor Änderungen

Schwächen:

  • Go-Binary erforderlich
  • Steilere Lernkurve
  • Komplexer für einfache Anwendungsfälle

Ideal für: Komplexe Multi-Machine Setups mit Secrets.

1
2
3
4
# chezmoi Beispiel
chezmoi init
chezmoi add ~/.zshrc
chezmoi apply

yadm

Stärken:

  • Git-native (ist ein Git-Wrapper)
  • Alternate Files für verschiedene Hosts
  • Einfach wenn du Git kennst
  • Bootstrap-Scripts

Schwächen:

  • Requires Git (aber das hast du eh)
  • Alternate Files können unübersichtlich werden

Ideal für: Git-Power-User, die ihre Dotfiles direkt in ~ verwalten wollen.

1
2
3
4
# yadm Beispiel
yadm init
yadm add ~/.zshrc
yadm commit -m "Add zshrc"

Wann welches Tool?

Szenario Empfehlung
Einfaches Setup, wenige Configs GNU Stow
Multi-Machine mit gleichen Configs GNU Stow (mit Paketen)
Templates für Host-spezifische Werte chezmoi
Secret-Management erforderlich chezmoi
Git-Power-User, direkte Home-Verwaltung yadm
Minimale Dependencies GNU Stow

Cheat Sheet

Basis-Befehle

Befehl Beschreibung
stow <pkg> Symlinks für Paket erstellen
stow -D <pkg> Symlinks entfernen
stow -R <pkg> Restow (Delete + Stow)
stow */ Alle Pakete stow’en
stow -n <pkg> Dry-Run
stow -v <pkg> Verbose Output

Pfad-Optionen

Befehl Beschreibung
stow -d DIR <pkg> Stow Directory angeben
stow -t DIR <pkg> Target Directory angeben
stow -d ~/dotfiles -t ~ zsh Vollständiges Beispiel

Fortgeschrittene Optionen

Befehl Beschreibung
stow --adopt <pkg> Existierende Dateien ins Paket übernehmen
stow --no-folding <pkg> Keine Verzeichnis-Symlinks
stow --ignore='REGEX' <pkg> Dateien ausschließen
stow --override='REGEX' <pkg> Konflikte überschreiben
stow --defer='REGEX' <pkg> Konflikte überspringen

Dateien

Datei Funktion
.stowrc Default-Optionen
.stow-local-ignore Ignore-Regeln pro Paket
.stow-global-ignore Globale Ignore-Regeln

Fazit

GNU Stow ist das perfekte Tool für alle, die ihre Dotfiles einfach und zuverlässig verwalten wollen. Mit einem einzigen Befehl ersetzt es dutzende manuelle ln -sf-Aufrufe und macht das Dotfiles-Management zum Kinderspiel.

Die wichtigsten Punkte:

  1. Struktur ist alles: Pakete müssen die Zielstruktur spiegeln
  2. Dry-Run nutzen: Immer erst mit -n testen
  3. Einfach starten: Beginne mit wenigen Paketen und erweitere nach Bedarf
  4. Git nicht vergessen: Dotfiles gehören versioniert!

Mein Tipp: Starte mit einem einfachen Setup (zsh, git, tmux) und füge nach und nach weitere Pakete hinzu. Nach einer Woche wirst du dich fragen, wie du jemals ohne Stow ausgekommen bist!


Ressourcen

Dieser Eintrag ist vom Autor unter CC BY 4.0 lizensiert.