Dotfiles 统一管理:11 台跨平台设备的 symlink + Git 方案
系列文章:本文是「Gemini 灾难恢复」系列的第 2 篇。
- Gemini CLI 误删 Home 目录:cd 失败 + find -delete 的事故与恢复
- Dotfiles 统一管理:11 台跨平台设备的 symlink + Git 方案(本文)
- macOS 灾后状态恢复:Keychain 重建、Syncthing 重配对、OneDrive 冲突处理
本文记录一套面向 11 台设备的 dotfiles 统一管理方案。所有设备都在同一个 Tailscale 网络里,通过 hostname 互相访问;其中实际纳入统一管理的是前 7 台设备,覆盖 .zshrc、.gitconfig、starship.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 这类特殊系统仍然需要分开处理,但主要开发设备的初始化流程已经明显收敛。下次再出问题,恢复工作会更接近”执行脚本并补少量差异”,而不是从空白开始重建环境。