Dotfiles 统一管理:11 台跨平台设备的 symlink + Git 方案

2026年3月15日 | Ruichen Zhou | 约 10 分钟阅读

系列文章:本文是「Gemini 灾难恢复」系列的第 2 篇。

  1. Gemini CLI 误删 Home 目录:cd 失败 + find -delete 的事故与恢复
  2. Dotfiles 统一管理:11 台跨平台设备的 symlink + Git 方案(本文)
  3. macOS 灾后状态恢复:Keychain 重建、Syncthing 重配对、OneDrive 冲突处理

本文记录一套面向 11 台设备的 dotfiles 统一管理方案。所有设备都在同一个 Tailscale 网络里,通过 hostname 互相访问;其中实际纳入统一管理的是前 7 台设备,覆盖 .zshrc.gitconfigstarship.toml、SSH config、wezterm.lua 和 VS Code settings.json,两台 OpenWrt 路由器与两部手机单独处理。

设备清单与管理范围

当前设备清单如下:

  • air: MacBook Air,主力便携开发机,macOS,在德国宿舍和学校之间通勤
  • macmini: Mac mini,家里的常驻 macOS 设备,偶尔远程上去处理事情
  • tower: 组装台式机,Windows + WSL,重度使用,日常写代码和跑实验
  • thinkbook: ThinkBook 笔记本,Windows,放在德国宿舍
  • thinkpad: ThinkPad,装了 Linux Mint,中国那边的主要开发工作站
  • server: 家里的服务器,Linux,跑各种自托管服务
  • vps: 云服务器,Linux,主要跑 DERP 节点和一些对外服务
  • router-de: 德国宿舍的 OpenWrt 路由器
  • router-sd: 家里的 OpenWrt 路由器
  • phone-1, phone-2: 两部手机,主要就是 Tailscale 客户端

真正需要纳入 dotfiles 仓库统一管理的是前 7 台。两个路由器的配置结构和常规 Linux 差异较大,手机也不适合放进同一套配置同步流程里。

Dotfiles 仓库怎么组织

Dotfiles 仓库按工具分目录,目标是让每类配置文件有稳定路径,再通过 symlink 映射到系统期望位置。

纳入管理的文件清单:

  • .zshrc(shell 配置)
  • .gitconfig(Git 全局配置)
  • starship.toml(终端 prompt)
  • SSH config
  • WezTerm 配置(wezterm.lua
  • VS Code 的 settings.json

目录结构按工具分:

dotfiles/
├── shell/
│   └── .zshrc
├── git/
│   └── .gitconfig
├── ssh/
│   └── config
├── wezterm/
│   └── wezterm.lua
├── vscode/
│   └── settings.json
├── starship/
│   └── starship.toml
└── install.sh

跨平台 symlink 是最核心的策略。macOS 和 Linux 上直接 ln -s

ln -sf ~/dotfiles/shell/.zshrc ~/.zshrc
ln -sf ~/dotfiles/git/.gitconfig ~/.gitconfig
ln -sf ~/dotfiles/starship/starship.toml ~/.config/starship.toml

Windows 上用 PowerShell:

New-Item -ItemType SymbolicLink -Path "$env:USERPROFILE\.gitconfig" -Target "D:\dotfiles\git\.gitconfig"

最后写了个 install.sh,clone 完仓库跑一遍就把所有 symlink 建好。新机器到手,git clone 加一个 ./install.sh,基本就能用了。

SSH Config 大整理

SSH config 的核心目标是统一命名和统一入口。旧配置里如果使用随手起的别名,跨设备迁移时很难记住每个 Host 对应哪台机器、哪个端口,也不利于后续维护。

现在统一用 Tailscale hostname 作为 SSH Host 名:

Host thinkpad
  HostName thinkpad
  User ruichen
  Port 55905

Host server
  HostName server
  User ruichen
  Port 22

Host vps
  HostName vps
  User root
  Port 22

名字就是机器名,不用额外记忆,也不容易混淆。

比较复杂的是多通道的问题。同一台机器可能有好几种连法:Tailscale 直连是最优先的,ZeroTier 作为备用,某些场景下还需要 FRP 公网穿透。我用不同的 Host 条目来区分:

Host thinkpad
  HostName thinkpad
  ProxyCommand nc -X 5 -x router-de:1080 %h %p

Host thinkpad-frp
  HostName my-frp-server.example.com
  Port 6001
  User ruichen

从外部网络连回家的时候,流量路径大概是这样的:笔记本 → Tailscale SOCKS5(路由器上的)→ 目标设备。ProxyCommand 把这条链路封装起来,日常用的时候只管敲 ssh thinkpad

各种 App 的配置同步

配置文件不只是 shell 和 SSH,还有一批应用层面的内容需要一起统一。

Rime 输入法的词库同步已经单独跑了一套稳定方案。核心思路是在 installation.yaml 里配 sync_dir 指向 OneDrive 路径,macOS 的鼠须管和 Windows 的小狼毫各自同步到同一个目录下。每台机器用自己的 installation_id 做子目录,不会互相覆盖。这个方案跑了几个月了,一直很稳。

VS Code 是个比较特殊的情况。我同时用稳定版和 Insiders 两套,各自有独立的 Settings Sync。稳定版主要做远程开发,Insiders 做本地和 LaTeX。它们的 settings 大部分相同但有关键差异(比如远程版关掉了 git.autorefresh,Insiders 多了 LaTeX Workshop 的配置),所以不能合成一套。Settings Sync 管了大部分,但有些 keybindings 还是靠手动复制。

WezTerm 的配置文件是 Lua,天然适合 symlink。直接把 dotfiles 仓库里的 wezterm/wezterm.lua 链接到 WezTerm 期望的路径就行。所有用 WezTerm 的设备看到的终端配置都是同一份。

Starship prompt 也一样,starship.toml 统一管理,symlink 到 ~/.config/starship.toml。这样不管我在哪台机器上打开终端,看到的 prompt 样式都是一样的。这种一致性本身就是价值,它能减少环境切换时的心智负担。

现在到什么程度了

目前大部分设备已经用上了 dotfiles 仓库。新机器到手的流程变成了:装好系统 → 装 Git → git clone./install.sh → 基本可用。

但还有几个没解决的问题。

Windows 上建 symlink 需要管理员权限,每次都要“以管理员身份运行 PowerShell”才能执行 New-Item -ItemType SymbolicLink,这一步挺烦的。虽然可以开启开发者模式来绕过,但我总觉得不够干净。

路由器上的配置没法纳入同一个仓库。OpenWrt 的文件系统结构和常规 Linux 差太多,/etc/config/ 下面那些东西放进 dotfiles 仓库也没什么意义。这部分我还是手动管理,最多做个备份。

VS Code 的 extensions 列表还是手动管的。虽然可以用 code --list-extensions 导出,再用 code --install-extension 批量安装,但我一直没把这个流程脚本化。每次新机器还是手动装插件,不优雅但也不算太痛苦。

这套方案的目标不是让 11 台设备完全同构,而是给核心配置建立一个 single source of truth。平台差异、权限限制和 OpenWrt 这类特殊系统仍然需要分开处理,但主要开发设备的初始化流程已经明显收敛。下次再出问题,恢复工作会更接近”执行脚本并补少量差异”,而不是从空白开始重建环境。

评论