Armbian 自动挂载 USB 移动硬盘

Armbian 自动挂载 USB 移动硬盘
狂犬主子最近在折腾配置 N1 上的 Armbian,想实现插入 USB 移动硬盘自动挂载的功能。经过一番摸索,终于找到了一套稳定可靠的方案,特此记录下来,供大家参考。
AI 生成的 Armbian USB 移动硬盘自动挂载完全指南。
基于 udev + systemd 的稳定热插拔方案,同时覆盖 NTFS 与 exFAT 两种文件系统。
适用 Armbian current / edge(内核 ≥ 5.15)。
前置问题:驱动该用哪个?
在开始配置之前,先把驱动问题说清楚,因为 NTFS 和 exFAT 的情况截然不同。
NTFS:ntfs3 vs ntfs-3g
Linux 5.15 起内核自带 ntfs3 驱动(Paragon 贡献),Armbian 已安装的 ntfs-3g 是早年 FUSE 方案。
| 维度 | ntfs3(内核驱动) | ntfs-3g(FUSE 驱动) |
|---|---|---|
| 运行层 | 内核空间 | 用户空间(FUSE) |
| 读写性能 | 快约 20-25% | 较慢(上下文切换开销) |
| 稳定性 | 成熟(5.15 后持续完善) | 久经考验(2007 年起) |
Type= 参数 |
ntfs3 |
ntfs-3g |
| 是否需要额外安装 | 否(内核自带) | 是(已安装) |
结论:挂载改用 ntfs3,但保留 ntfs-3g 安装。
ntfs-3g 包含 ntfsfix 工具,在硬盘出现 dirty 标记时必须用它修复。工具留着,驱动换掉。
1 | # 确认 ntfs3 模块存在(应输出 ✅) [ -d "/lib/modules/$(uname -r)/kernel/fs/ntfs3" ] \ && echo "✅ ntfs3 available" \ || echo "❌ 内核不含 ntfs3,请改用 ntfs-3g" |
exFAT:内核 exfat vs exfatprogs
同理,Linux 5.4 起内核已内置 exFAT 驱动。exfatprogs(或旧版 exfat-utils)是用户空间工具包,提供 mkfs.exfat、fsck.exfat 等命令,挂载本身不需要它,但建议保留。
1 | # 确认内核 exfat 模块 modinfo exfat | grep filename # 安装工具包(如果还没装) apt install exfatprogs |
挂载 exFAT 直接用 Type=exfat,无需 FUSE 层。
方案架构
整个方案由两个部件组成:
1 | USB 插入/拔出 │ ├─ udev rule 捕获块设备事件(by UUID) │ ↓ ├─ 触发 systemctl start/stop │ ↓ └─ systemd .mount unit 执行挂载/卸载 │ ├─ nofail → 开机无盘不阻塞 └─ DefaultDependencies=no → 不产生循环依赖 |
Step 1:确认硬盘 UUID
始终用 UUID,不用 /dev/sdX。 设备名随插入顺序变化,UUID 是硬盘的永久标识。
1 | # 插入硬盘后执行 lsblk -f # 或用 blkid 获取完整信息 blkid | grep -E "ntfs|exfat" # 示例输出: # /dev/sda1: UUID="A1B2C3D4E5F67890" TYPE="ntfs" ... # /dev/sda1: UUID="1A2B-3C4D" TYPE="exfat" ... |
注意:NTFS 的 UUID 通常是 16 位十六进制(如
A1B2C3D4E5F67890),
exFAT 的 UUID 通常是 8 位带短横线(如1A2B-3C4D),两者格式不同,注意区分。
Step 2:创建挂载点
1 | mkdir -p /mnt/usbdisk |
挂载点命名与 unit 文件名必须对应:
/mnt/usbdisk→ unit 文件名为mnt-usbdisk.mount(斜杠换短横线)。
如果你改成/mnt/my_disk,unit 文件名就要改成mnt-my_disk.mount。
Step 3:编写 systemd .mount unit
根据你的文件系统类型,选择对应的配置。
NTFS 版本
1 | # /etc/systemd/system/mnt-usbdisk.mount [Unit] Description=USB NTFS External Disk After=local-fs.target DefaultDependencies=no [Mount] What=/dev/disk/by-uuid/A1B2C3D4E5F67890 Where=/mnt/usbdisk Type=ntfs3 Options=defaults,nofail,noatime,uid=1000,gid=1000,umask=0022,windows_names [Install] WantedBy=multi-user.target |
exFAT 版本
1 | # /etc/systemd/system/mnt-usbdisk.mount [Unit] Description=USB exFAT External Disk After=local-fs.target DefaultDependencies=no [Mount] What=/dev/disk/by-uuid/1A2B-3C4D Where=/mnt/usbdisk Type=exfat Options=defaults,nofail,noatime,uid=1000,gid=1000,umask=0022 [Install] WantedBy=multi-user.target |
关键选项说明
| 选项 | 作用 | 是否必须 |
|---|---|---|
nofail |
挂载失败不阻塞开机 | 必须 |
DefaultDependencies=no |
避免循环依赖导致开机卡住 | 必须 |
noatime |
不更新访问时间,减少写入 | 推荐 |
uid=1000,gid=1000 |
文件属主(替换为你的实际 UID) | 推荐 |
umask=0022 |
文件权限 755/644 | 推荐 |
windows_names(仅 ntfs3) |
避免 Windows 非法文件名冲突 | 推荐 |
1 | # 查询你的 UID id your_username |
Step 4:编写 udev 热插拔规则
systemd mount unit 只处理开机挂载。要实现插入自动挂载、拔出自动卸载、重插后再次挂载,需要 udev 规则来监听块设备事件。
1 | # /etc/udev/rules.d/99-usb-mount.rules # 插入时挂载(替换 UUID 为你的实际值) ACTION=="add", SUBSYSTEM=="block", \ ENV{ID_FS_UUID}=="YOUR-UUID-HERE", \ RUN+="/bin/systemctl start mnt-usbdisk.mount" # 拔出时卸载(防止下次插入时出现 dirty 标记) ACTION=="remove", SUBSYSTEM=="block", \ ENV{ID_FS_UUID}=="YOUR-UUID-HERE", \ RUN+="/bin/systemctl stop mnt-usbdisk.mount" |
两处 UUID 都要替换,且必须与 mount unit 中的完全一致(大小写敏感)。
udev 通过
ENV{ID_FS_UUID}匹配,即使同时插多块 USB 盘也不会混淆。
Step 5:启用配置
1 | # 重新加载 systemd 配置 systemctl daemon-reload # 重新加载 udev 规则(无需重启) udevadm control --reload-rules udevadm trigger # 手动测试挂载 systemctl start mnt-usbdisk.mount systemctl status mnt-usbdisk.mount # 确认挂载成功 df -h /mnt/usbdisk # 设置开机自动挂载(硬盘存在时生效) systemctl enable mnt-usbdisk.mount |
Step 6:三种场景验证
场景一:开机不插盘
1 | reboot # 开机后检查,状态应为 inactive (dead),而非 failed systemctl status mnt-usbdisk.mount |
场景二:开机后插盘
1 | # 插入硬盘,等 2 秒后验证 ls /mnt/usbdisk # 实时查看 udev 触发日志 journalctl -f | grep -E "usb|usbdisk|ntfs|exfat" |
场景三:掉盘模拟(拔出后重插)
1 | # 开启实时日志监控 journalctl -f # 拔出硬盘 → 等 3 秒 → 重新插入 # 日志中应先出现 stop 再出现 start # 之后 /mnt/usbdisk 应再次可访问 ls /mnt/usbdisk |
常见问题排查
问题一:挂载失败,日志显示 “dirty”
Windows 未安全弹出,或 Linux 上次拔盘前未卸载,NTFS 会被标记为 dirty,内核 ntfs3 默认拒绝挂载。exFAT 无此问题。
1 | # 用 ntfs-3g 工具包修复(这就是保留它的意义) ntfsfix /dev/sdX1 # 修复后重新挂载 systemctl start mnt-usbdisk.mount |
问题二:udev 规则不触发
1 | # 检查 udev 是否识别到 UUID udevadm info --name=/dev/sda1 | grep ID_FS_UUID # 手动测试规则匹配 udevadm test $(udevadm info -q path -n /dev/sda1) 2>&1 | grep -E "uuid|RUN" |
问题三:开机仍卡住约 90 秒
确认以下两项同时存在,缺一不可:
[Unit]段有DefaultDependencies=noOptions=中有nofail
问题四:中文文件名乱码
- ntfs3:默认已是 UTF-8,通常无需额外设置。若仍乱码,在 Options 中加
iocharset=utf8。 - exFAT:同上,内核驱动默认 UTF-8。
问题五:确认当前实际使用的驱动
1 | # 查看挂载点使用的文件系统类型 mount | grep usbdisk # 或 findmnt /mnt/usbdisk |
问题六:Docker 指定用户运行的程序出现权限问题,无法访问挂载的文件
容器指定运行用户 1001:1001,出现可读不可写的问题。
Docker Compose 层面无法绕过 exFAT 挂载权限,因为权限不是文件系统里存的元数据,而是挂载时内核固定写死的。exFAT 本身就没有真正的 Linux 权限模型。它是为跨平台设计的(Windows、相机、移动设备都用),压根不存储 uid/gid/权限位,所有权限都是挂载时内核伪造出来的静态值。所以在 exFAT 上纠结权限精细控制,本身就是在和文件系统的设计目标对着干。
必须改挂载参数,最省事的就是在 mount unit 里加一个字:
1 | Options=defaults,nofail,noatime,uid=1000,gid=1000,umask=0000 # 之前是 umask=0022 |
改完:
1 | systemctl restart mnt-usbdisk.mount |
compose 文件完全不用动。这是 exFAT 的本质限制,不是 Docker 的问题。
umask=0000 是什么意思
umask 是"权限遮罩",它的值表示要从默认权限中去掉哪些位。
默认权限是:
- 目录:
777(rwxrwxrwx) - 文件:
666(rw-rw-rw-)
然后用默认权限减去 umask,得到实际权限。umask 是"权限遮罩",它的值表示要从默认权限中扣除哪些位。
简单说:
umask 是一个遮罩,告诉系统从默认权限里去掉哪些位。
- 默认权限是 777(目录),666(文件)
umask=0022→ 去掉组和其他人的写权限 → 得到 755 / 644umask=0002→ 只去掉其他人的写权限 → 得到 775 / 664(组成员可写)umask=0000→ 什么都不去掉 → 得到 777 / 666(所有人可读写)
容器里 1001:1001 的进程,相对于挂载时指定的 uid=1000,gid=1000 来说,属于 “其他用户” 这一类。umask=0022 给其他用户的写权限是 0,所以只读。改成 umask=0000 就是把其他用户的写权限留着,1001 就能写了。
umask=0000 的安全隐患
umask=0000 相当于给整块盘开了 777,任何本地用户、任何容器进程都能写,理论上存在安全隐患。但在家庭 NAS 场景下,这通常不是大问题。如果你对安全性有更高要求,exFAT 可能就不适合了,因为它根本不支持真正的权限控制。
如果这块盘只在 Linux 上用,不需要插到 Windows 或其他设备上,换成 ext4 或 btrfs 是更正确的选择。原因很直接:
exFAT 的权限是假的,内核挂载时伪造的静态值,无法给不同文件设置不同权限,无法用 chown,无法用 chmod。多个容器用不同 uid 访问同一块盘,你永远在和挂载参数做妥协。
ext4 vs btrfs 怎么选:
| ext4 | btrfs | |
|---|---|---|
| 稳定性 | 极成熟,几乎零意外 | 成熟,但有坑(RAID5/6 别用) |
| 移动硬盘场景 | ✅ 够用,简单可靠 | ✅ 快照、压缩、校验更强 |
| 突然断电/掉盘 | 日志保护,恢复快 | CoW 设计,数据更安全 |
| 适合 NAS 多容器 | ✅ | ✅ 更适合(快照备份方便) |
| 格式化难度 | 极简单 | 简单 |
个人 NAS 场景我会推荐 btrfs,主要原因是移动硬盘最怕意外断电,btrfs 的 CoW 写入机制比 ext4 更能保护数据完整性,而且以后可以对重要目录做快照。
完整文件速查
NTFS 用户的文件清单
/etc/systemd/system/mnt-usbdisk.mount
1 | [Unit] Description=USB NTFS External Disk After=local-fs.target DefaultDependencies=no [Mount] What=/dev/disk/by-uuid/YOUR-NTFS-UUID Where=/mnt/usbdisk Type=ntfs3 Options=defaults,nofail,noatime,uid=1000,gid=1000,umask=0022,windows_names [Install] WantedBy=multi-user.target |
/etc/udev/rules.d/99-usb-mount.rules
1 | ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_UUID}=="YOUR-NTFS-UUID", RUN+="/bin/systemctl start mnt-usbdisk.mount" ACTION=="remove", SUBSYSTEM=="block", ENV{ID_FS_UUID}=="YOUR-NTFS-UUID", RUN+="/bin/systemctl stop mnt-usbdisk.mount" |
exFAT 用户的文件清单
/etc/systemd/system/mnt-usbdisk.mount
1 | [Unit] Description=USB exFAT External Disk After=local-fs.target DefaultDependencies=no [Mount] What=/dev/disk/by-uuid/YOUR-EXFAT-UUID Where=/mnt/usbdisk Type=exfat Options=defaults,nofail,noatime,uid=1000,gid=1000,umask=0022 [Install] WantedBy=multi-user.target |
/etc/udev/rules.d/99-usb-mount.rules
1 | ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_UUID}=="YOUR-EXFAT-UUID", RUN+="/bin/systemctl start mnt-usbdisk.mount" ACTION=="remove", SUBSYSTEM=="block", ENV{ID_FS_UUID}=="YOUR-EXFAT-UUID", RUN+="/bin/systemctl stop mnt-usbdisk.mount" |
参考
https://www.freedesktop.org/software/systemd/man/systemd.mount.html
适用:Armbian current · edge · Kernel ≥ 5.15




