Skip to content

挂载迁移:容器数据怎么放到外置盘 ​

一句话结论

~/Library/Containers/ 和 ~/Library/Group Containers/ 里的数据(微信聊天记录、QQ 音乐缓存、App Store 应用的数据)不能用符号链接搬走。AppPorts 1.9.0 起改为:在外置盘上新建一个专用的 APFS 数据卷,把数据复制进去,再把这个卷挂载到原来的目录上。应用看到的路径没变,签名没动。

前提三条:外置盘是未加密的 APFS;第一次打开应用时点允许授权框;打开应用前先连接外置盘。

什么时候会用到 ​

在「数据目录」→「应用数据」里选中一个应用,Containers 和 Group Containers 分组下的目录,按钮是「挂载迁移」而不是「迁移」。其它分组(Application Support、缓存等)和工具目录、自定义目录照旧用符号链接迁移,不受影响。

为什么容器目录特殊、为什么不看主程序是不是沙盒,见容器数据、沙盒与签名身份。

点「挂载迁移」之后 ​

AppPorts 先只读检查一次外部存储,不改动任何东西,再按结果给出下一步:

检查结果你会看到可以怎么做
未加密的 APFS,空间足够迁移说明:本机能释放多少空间、第一次打开要点允许、平时要连着盘点「迁移数据」开始
exFAT、NTFS、HFS+ 等「这块外部存储是 exFAT 格式」保留现状;选择其他位置;查看准备方法
加密的 APFS「这块外部存储已加密」保留现状;改选未加密的 APFS 位置
空间不足需要多少、还剩多少清理空间后重新检查,或选择其他位置
没选外部存储 / 没连接提示选择或连接外部存储选择外部存储、重新检查

不能迁移时什么都不用改。 应用本体、Application Support、缓存、工具目录照常可以迁移到这块盘,只有容器数据留在本机,应用照常使用。以后想迁移时,按准备 APFS 外置盘的方法准备好再来。

为什么加密的 APFS 盘暂不支持

挂载迁移新建的数据卷不会继承原卷的密码。照常迁移的话,原本在本机受 FileVault 保护的聊天记录会落在一个没有密码的卷上。自动解锁和密码管理完成之前,AppPorts 不做这种悄悄降级。详见加密的外置盘。

迁移前 ​

  • 请先把 AppPorts 放进「应用程序」文件夹,再从那里打开。 登录代理需要一个持久有效的程序路径。从下载目录或 DMG 直接运行时,macOS 可能使用临时的 App Translocation 路径;检测到这种路径时,AppPorts 会阻止新的挂载迁移并提示安装。移动或更新 AppPorts 后请打开一次,让它校准代理路径;路径不变时不会重新加载代理。
  • 完全退出要迁移的应用。 AppPorts 会检查,正在运行时不允许迁移。
  • AppPorts 需要完全磁盘访问权限。 把卷挂到容器路径这个动作本身受系统管控,没有权限会失败。
  • 想想备份。 和所有数据迁移一样,重要数据建议先自行备份一份。迁移后这部分数据在外置盘上,而 Time Machine 通常不备份外置盘,需要时请在「系统设置 › 通用 › 时间机器」的选项里确认这块盘在备份范围内。

迁移过程中发生了什么 ​

  1. 在外置盘的 APFS 容器里新建一个卷,建好后不自动挂到 /Volumes。卷名形如 AppPorts-<Bundle ID>-<目录名>-xxxxxx,和盘上其它卷共用剩余空间,不用指定大小。
  2. 把新卷临时挂到 ~/Library/Application Support/AppPorts/mounts/ 下,用 AppPorts 的复制器把目录内容复制进去,在卷根写一个 .appports-mount-metadata.plist 标记,然后卸载。
  3. 把原目录改名为同卷安全备份,在原路径新建一个空目录,把卷挂上去,校验挂上的确实是这个卷。
  4. 写入挂载记录 ~/Library/Application Support/AppPorts/container-mounts.plist,安装登录时自动重挂的代理,最后删除安全备份。

外置盘空间不够时,在建卷之前就停下。迁移尚未完成就失败时,AppPorts 会尝试回滚;如果无法安全完成回滚,会保留副本并说明保留路径。

如果迁移已经完成,只是最后清理本地安全备份失败,挂载的数据仍可正常使用。AppPorts 会明确显示尚未清理的备份路径,无需重新迁移。

迁移完成后,mount 命令能看到卷直接挂在容器路径上:

/dev/disk7s5 on /Users/<user>/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files (apfs, local, nodev, nosuid, journaled, noowners, nobrowse)

迁移后第一次打开应用 ​

系统会弹出「"某某"想要访问可移动宗卷上的文件」,点允许。这是 macOS 对数据落在外置盘上的正常检查,只弹一次。

点了拒绝,应用会当作没有数据,显示空白。补救:系统设置 → 隐私与安全性 → 文件和文件夹(或「可移动宗卷」),找到该应用打开开关,或者在终端执行 tccutil reset SystemPolicyRemovableVolumes <Bundle ID> 让它下次重新询问。

/System/Applications 下的系统应用不会弹框,直接静默拒绝。AppPorts 本来就不迁移它们。

日常使用 ​

打开应用前先连接外置盘。 盘不在时,挂载点只是一个被锁住的空目录(权限 000),应用会看到空数据,不会报错,也不会把新数据写到本机形成两份。连上外置盘后 AppPorts 会自动把卷接回来,数据就回来了。

Finder 里平时看不到这些数据卷。 AppPorts 挂载时带 nobrowse 选项,数据卷不出现在 Finder 边栏和桌面。外置盘刚接入的一两秒内,macOS 可能先把卷自动挂到 /Volumes 并短暂显示图标,AppPorts 接回后就会消失。用早期版本迁移、目前仍显示在 Finder 里的卷,AppPorts 下次启动或外置盘下次接入时会原地隐藏,不需要卸载重挂。「磁盘工具」里仍能看到名为 AppPorts-… 的卷,不要在那里抹掉或删除它们,那就是迁移过去的数据。

拔盘前先退出应用,再在 AppPorts 里点「卸载」或在 Finder 里推出外置盘。 直接拔盘的话,最后几秒钟的写入可能丢失,数据库文件可能需要修复;我们做过拔盘测试,APFS 卷这种情况下丢的只是最后几个事务,见实验记录:拔盘测试。

什么时候自动接回:

  • AppPorts 运行时:启动时以及每次有卷接入系统时,自动把在线但没挂载的记录挂回去。
  • 没开 AppPorts 时:迁移成功后 AppPorts 会安装一个登录代理,登录时以及每次外置盘接入时静默挂回,然后退出。最后一条记录被还原后代理自动卸载。
  • 开机后立刻打开应用,仍可能读到十几秒的空目录(系统把登录代理排在登录项之后启动),退出应用再打开即可。兜底的是挂载点一直保持为空:应用就算先起来了,也只读到空目录,不会把新数据写到本机形成分叉,等卷挂回来自己就恢复了(微信实测三次都能恢复)。

macOS 12 等旧系统需要输入管理员密码

macOS 27 允许普通用户把卷挂到自己目录下的路径;macOS 12 实测不允许。AppPorts 遇到这个错误会弹系统的管理员密码框重试,一次迁移通常只问一次。登录代理没有界面,没法弹密码框,所以在这类系统上登录后不能自动重挂,需要打开 AppPorts(它会提示输密码)或在「应用数据」页手动点「挂载」。13 到 26 之间从哪一版开始放开,还没有逐一验证。

状态与操作 ​

状态含义可用操作
已挂载卷挂在原目录上,应用正常读写卸载、还原
待挂载卷在线但没挂载(刚插盘、或手动卸载过)挂载、还原
外置盘未连接找不到这个数据卷,多半是外置盘没连接连上外置盘,AppPorts 会自动接回;已经连接仍显示时,见下方排查

还原会把卷上的数据复制回本机,然后删除卷和记录,外置盘要保持连接:

  • 开始前检查本机剩余空间,不够时直接停下,卷和记录原样保留。
  • 复制完成后,AppPorts 先把挂载记录转为待清理信息,防止自动重挂,再切换回本地目录。它只删除卸载后留下的空挂载点,不会递归删除任何目录。
  • 如果切换回本地目录未完成,暂存的本地副本和外置卷都会保留。本地副本仍在同一目录下名为 .appports-restore-staging-… 的隐藏文件夹里;请按 AppPorts 显示的保留路径和提示处理。
  • 如果本地目录已经恢复,只是删除外置卷或更新清理记录失败,AppPorts 会明确提示还原完成但清理未完成。可在数据目录页重试清理,不要重复迁移或还原。

无法确认副本是否仍在时,可选择「仅移除清理记录」;这不会删除本地备份或外置卷,也不会重新挂载,仍存在的副本需自行清理。

如果挂载点目录里出现了本地文件(比如应用在盘不在时想办法写了东西),AppPorts 会拒绝在上面挂载,免得盖住这份数据。把文件移走再挂载。

卸载或移动 AppPorts 之前 ​

挂载迁移靠 AppPorts 的登录代理在每次登录后把卷接回来。删除 AppPorts 之前,先在「应用数据」里把挂载迁移的目录「还原」回本机。 不还原就删除 AppPorts 的话,数据仍完好地留在外置卷上,但登录后没人把它接回来,应用会看到空目录;重新安装 AppPorts 并打开一次即可恢复。

只是更新或移动 AppPorts 的话,打开一次新版本即可,它会自动把代理指向新的程序位置。

排查 ​

现象处理
状态是「外置盘未连接」,但盘已经连上在「磁盘工具」里确认外置盘上还有 AppPorts-… 的卷。卷还在:点 AppPorts 工具栏的刷新,或重新插拔一次外置盘。卷已被删除:这个目录的数据已经不在外置盘上,只能从备份恢复
应用打开后是空的先看该目录是不是「已挂载」;不是就连上外置盘或点「挂载」。是「已挂载」仍为空,检查第一次打开应用时有没有点了拒绝
点「挂载」提示挂载点不为空挂载点目录里有本地文件。确认这些文件是什么,把它们移走后再挂载
迁移或还原提示后台正在连接外部存储登录代理正在挂载,稍等几秒再点一次

为什么不用磁盘映像 ​

exFAT 用户不少,我们认真试过在 exFAT 盘上放一个 APFS 磁盘映像(sparsebundle)再挂载的办法。它不弹授权框、性能相当、任何格式都能放,看起来更好。

然后做了拔盘测试:写入数据的时候直接拔 U 盘。APFS 卷只丢最后几个事务,数据库能修复;磁盘映像两轮都整个打不开,里面的数据全部失联。原因是映像自己的"目录簿"每隔几秒重写一次,拔盘正好撞上写一半,整个映像就成了没有目录的碎片。完整数据见实验记录:拔盘测试,面向用户的解释见为什么外置盘必须是 APFS。

所以只支持 APFS 卷。

技术细节 ​

自动重挂的时序 ​

  • 登录代理是 ~/Library/LaunchAgents/com.shimoko.AppPorts.container-mount.plist,运行 AppPorts --mount-agent。它同时监听 /Volumes:外置盘一挂上系统就再跑一次。实测这次开机(2026-09-22)的顺序是「登录完成 → 4.6 秒后代理启动 → 18 秒后两个卷挂回容器路径」,比早先版本快了约 19 秒,但登录项里的应用约 3 秒就起来了。时间线和为什么不能更早,见实验记录:开机前挂载。
  • 盘迟迟没出现时,代理不会只试一遍就走:它在进程内盯着 /Volumes,等下一次真实变化再试,而不是按固定间隔轮询 —— 开机时系统很忙,外置盘可能好几分钟后才被识别(实测 2026-09-23 那次是开机后 2 分 33 秒),定时轮询要么在最忙的时候空转 diskutil,要么错过应用启动。等不到事件时每 20 秒兜底复查一次,总窗口 180 秒;等卷期间不占锁。实测:卷出现到挂载完成约 1 秒。
  • 挂载前先看一眼 /Volumes/<卷名>:开机和插盘时系统几乎总是先把卷挂到那里,用 statfs 加一次卷根标记读取(微秒级)就能确认是不是我们的卷,省掉一次 diskutil info —— 2026-09-23 开机实测那次查询花了 9 秒(登录后系统正忙),是整条链路上最贵的一步。认不出来(卷名被系统改名、标记缺失)才退回 diskutil 查询。
  • 挂载之后要校验:diskutil mount -mountPoint 有个坑 —— 卷已经被系统挂上时(开机/插盘时 macOS 常抢先把它挂到 /Volumes),它会忽略挂载点参数、照样打印 mounted 并返回 0。所以每次挂载后都确认卷真的落在目标路径上;没落到位就重新查一次卷挂在哪,把它从 /Volumes 卸下来再挂(最多 3 轮)。2026-09-21 和 09-23 各出过一次这种情况,当时判定为失败并放弃,微信随后读到空目录。
  • 只动自己的卷:挂载、卸载、还原之前都核对挂载点上的 Volume UUID 是否与记录一致;路径上挂着别的卷时直接停下,不会卸载、覆盖或删除它。
  • 代理和 AppPorts 共用一把跨进程锁(~/Library/Application Support/AppPorts/operation.lock):AppPorts 正在做挂载迁移、卸载或还原时,代理会先让路(最多等 120 秒),等不到就跳过这一轮,留到下次插盘或下次登录。反过来,AppPorts 拿不到锁时不会开始操作,只提示稍后重试。锁只在每一轮挂载动作期间持有 —— 代理等卷的那几分钟不占锁,不会卡住你在 AppPorts 里的操作。
  • 迁移记录文件读不出来时,AppPorts 不会把它当成空记录覆盖写入,而是保留原文件并拒绝新增或删除记录。
  • 不用 /etc/fstab:实测 UUID= 形式挂不上,设备号形式插拔后会变,而且开机由 root 挂载同样受权限管控。

卷上的 Spotlight 索引 ​

系统会把挂在容器路径上的卷当成普通外置卷,给它建一份自己的索引。实测两个微信卷的 .Spotlight-V100 合计 110 MB,而且开机后还在重写。这些是应用数据,系统索引没有任何用处,所以:

  • 建卷后往卷根写一个空的 .metadata_never_index,mds 会跳过整个卷;写入时如果系统已经建好 .Spotlight-V100,一并删掉。
  • 标记跟着卷走:换盘位、还原挂载迁移都不用重做。
  • 在这个版本之前迁移过的卷,下次挂载时自动补上标记。
  • 标记不写回本机目录:还原时 .metadata_never_index 和 .fseventsd、.Spotlight-V100 一样被跳过。

自查命令 ​

bash
# 挂载记录
plutil -p ~/Library/Application\ Support/AppPorts/container-mounts.plist

# 当前挂载
mount | grep Containers

# 登录代理
launchctl print gui/$(id -u)/com.shimoko.AppPorts.container-mount

# 卷根有没有防索引标记;系统的索引状态应为 Indexing disabled
ls -la "<挂载点路径>/.metadata_never_index"
mdutil -s "<挂载点路径>"

# 手动重挂(要在有完全磁盘访问权限的终端里执行;旧系统前面加 sudo)
diskutil mount nobrowse -mountPoint "<挂载点路径>" <Volume UUID>

相关文档 ​

最近更新