
手机相册三年没清理照片、视频加上微信接收的文档已经快把 128G 的存储塞满了。网盘会员一年比一年贵免费额度又天天提醒我“空间不足”。去年年初我决定不再续费用一台小主机自建了家庭私有云盘半年实践下来不仅手机自动备份的问题解决了爸妈家里的老照片、客厅电视要看的剧也都能在一个系统里搞定。这篇记录不是产品说明书而是我从选硬件到踩坑收尾的完整复盘。内容包括为什么放弃成品 NAS、系统盘和数据盘怎么规划、容器服务如何部署、远程访问怎么做得既方便又安全以及那些差点让数据丢光的教训。适合想低成本入门 NAS、又不想被成品系统绑住的人参考也适合已经有一台小主机但不知道从哪里开始折腾的朋友。1. 选型逻辑为什么最后落在“N100小主机 开源系统”1.1 先把需求写清楚再谈配置家用私有云盘最怕一上来就买一堆硬件结果只当移动硬盘用。我动手前给自己列了一个需求清单每一条都对应一个真实使用场景手机照片和视频自动备份最好能和原来用手机相册的体验接近重要文档在电脑、手机、平板之间能方便存取局域网内可以直接播放下载好的电影和剧集人在外面时偶尔要拿家里的一份文件整机功耗尽量低能 7x24 小时开机不心疼电费。把需求列出来之后预算就比较清晰了。我给自己的范围是 1500 到 2200 元包含主机、内存、硬盘和系统盘。超出这个范围就失去“自己动手”的意义了。1.2 为什么没买成品 NAS也没用路由器 USB 方案先说成品 NAS。它的优点是省心开箱即用手机 APP 也比较成熟。但一旦涉及“我的需求特别一点”比如想跑自定义 Docker 服务、想调整存储布局、想换系统成品机器就会变得很难受。很多成品 NAS 的系统是高度定制的底层改了哪些东西不一定有完整文档出了问题排查起来很被动。另外那些配置稍微能看的四盘位机型价格也普遍在 2500 元以上性价比并不高。路由器 USB 方案我也试过两天。把移动硬盘插在路由器上手机和电脑确实能访问到文件但速度非常不稳定拷贝照片经常掉线某些格式的硬盘在路由器上识别不了重新插回电脑又要修复分区。这个方案适合临时共享几个文件做私有云盘完全不合格。所以我当时定下来的核心思路是用一台标准的 x86 小主机系统自己做主存储自己做主出了问题也知道每一步是怎么来的。1.3 最终配置与购买建议我的最终配置部件型号/规格用途主机N100 迷你小主机准系统整机功耗低核显支持硬解内存16G DDR4 笔记本内存跑多个Docker容器足够系统盘256G M.2 SSD安装系统、放Docker元数据数据盘14T 机械硬盘存放照片、电影、文档数据盘24T 机械硬盘定时备份盘N100 小主机是我对比了多款之后选的。它的整体功耗在 10W 到 20W 之间满载也就 25W 左右按一天 24 小时开机算一个月电费基本都在一杯奶茶钱以内。性能方面虽然是低功耗处理器但跑家庭私有云盘的核心服务绰绰有余特别是它自带 Intel 核显Jellyfin 做视频转码时可以走硬件加速很实用。有一个购买建议给后面动手的人机械盘尽量别图便宜买普通台式机盘。家庭云盘是长期通电运行的建议优先考虑 NAS 专用盘比如西部数据红盘 Plus 或希捷酷狼系列。它们的噪音控制和稳定性都更好一些。预算确实紧张的话监控盘也能用但没有保修期内数据安全的保障自己衡量。1.4 系统为什么选了 Debian而不是 TrueNAS 或 Windows系统选型我花了一点时间。TrueNAS 我很喜欢它的管理界面和 ZFS 文件系统但 ZFS 对内存有一定要求官方建议最少 8G 起步如果开去重和压缩还要更多。我这台小主机只有 16G 内存留给 ZFS 之后再跑 Docker 容器会变得很紧张。TrueNAS 对硬件也有兼容性要求网卡和硬盘控制器太新或太冷门都可能有兼容问题。Windows 做 NAS 的问题在于系统本身太笨重。动不动系统更新半夜自动重启磁盘性能也没有 Linux 下那么透明。而且 Windows 的授权本身也是一笔成本。最终我选了 Debian 12。Debian 的稳定性在服务器领域很有口碑软件包生态丰富安装完系统只需要一个很干净的基础环境剩下全部交给 Docker。对于不熟悉 Linux 命令行的朋友可以在 Debian 之上装一个 CasaOS 或类似的面板工具用网页界面管理 Docker 应用体验会平滑很多。2. 系统安装与存储规划真正影响后面半年使用体验的关键决策2.1 安装过程中容易被忽略的几个节点用 balenaEtcher 把 Debian 安装镜像写入 U 盘插上小主机开机按 Del 或 F2 进入 BIOS关掉 Secure Boot把启动顺序改为 U 盘第一。这一步看起来基础但很多新手卡在这里因为不同品牌主板的 BIOS 界面差异很大找不到 Secure Boot 选项或者改了启动顺序后直接黑屏。Debian 安装过程中有几个选择要注意软件源建议换成国内镜像源否则下载软件包会非常慢磁盘分区时手动操作把 M.2 SSD 分成一个根分区和一个 swap 分区机械硬盘暂时不分区等系统装完再用命令行处理。安装到最后一步会让你装桌面环境这里我直接不选桌面只保留标准系统工具和 SSH Server。家庭云盘不需要显示器键鼠全部通过 SSH 管理省下来的内存和 CPU 都留给容器服务。系统装好后第一件事是修改 SSH 配置把PermitRootLogin改成no创建一个日常用户并加入sudo组。这不算多余操作因为后面所有折腾都在这个用户下进行用一个高权限的 root 去跑各种容器容易把系统权限搞乱。2.2 文件系统选型数据盘我用了 Btrfs安装系统时SSD 系统盘我用了 ext4因为系统盘不需要频繁做快照ext4 最稳定遇到问题恢复手段也成熟。但数据盘我选了 Btrfs原因就一个快照能力。Btrfs 创建子卷快照非常快占用空间少。家里私有云盘最重要的不是举手机而是误删文件的时候能救回来。我每周给数据盘创建一个快照万一哪次整理目录把文件删错了可以直接从快照里拉出来。这个能力在 ext4 上要么靠外部备份要么靠第三方工具体验差很多。数据盘格式化和挂载命令非常直接# 用 blkid 确认新硬盘的设备名比如 /dev/sdb mkfs.btrfs -f -L data /dev/sdb # 挂载到 /data 目录 mount -t btrfs -o noatime,compresszstd:3 /dev/sdb /data挂载参数里的compresszstd:3是 Btrfs 的透明压缩对照片、文档这类资源很有效压缩率不错CPU 开销也可以接受。但要注意这个参数只对新写入的文件生效已经写入的数据不会自动压缩。快照脚本可以用一条命令完成btrfs subvolume snapshot -r /data /data/snapshots/$(date %Y%m%d)我会配合 cron 每周执行一次。恢复的时候只需要把快照目录里的文件拷贝回原位或者用btrfs subvolume delete删掉当前子卷再btrfs subvolume snapshot恢复操作起来非常顺手。2.3 两块硬盘为什么不组 RAID1而是用定时增量备份买了两块 4T 硬盘很多人第一反应是做 RAID1 镜像。我最终没有这么做原因不是 RAID 没用而是家庭场景下 RAID 防的问题太窄了。RAID1 只能防“单块硬盘物理损坏”。但家里私有云盘最常见的灾难是误删文件、系统崩溃、某个 Docker 容器的数据覆盖了另一个目录、勒索病毒扫描等。这些情况 RAID 一点忙都帮不上反而可能因为重建阵列过程中操作失误导致数据二次损坏。所以我的方案是4T 数据盘独立工作第二块 4T 盘作为备份盘每天凌晨由 cron 运行一次增量同步。这样既省电备份盘大部分时间可以休眠又能覆盖硬件损坏、误删、逻辑错误等多类故障。同步核心命令rsync -av --delete /data/ /backup/data/这里要提醒一下--delete这个参数是双刃剑。它在源目录同步时会把目标目录里多余的文件删掉。如果源目录因为挂载失败变成空目录备份目录也马上会被清空。我后面踩坑部分会详细写这件事总之在没有额外检查机制之前不要轻易加--delete。2.4 目录规范与自动挂载存储这件事如果不在一开始就把目录规划好后面会非常混乱。我的目录结构是/data/media电影、剧集、音乐的存放目录/data/documents重要文档、扫描件/data/photos手机照片的最终归档目录/data/backup备份盘挂载点/data/docker所有容器配置文件和数据目录。为了让机械硬盘开机自动挂载需要编辑/etc/fstab。先通过blkid查到两个数据盘的 UUID然后写入UUID你的数据盘UUID /data btrfs noatime,compresszstd:3 0 2 UUID你的备份盘UUID /backup ext4 noatime 0 2写完 fstab 后最好执行一次mount -a验证不要直接重启。如果 fstab 写错系统可能启动到 emergency 模式。我第一次就栽在这儿重启后卡在维护模式最后用维护模式的 root 账号把错误行删掉才恢复。目录权限也需要统一。我在/data下创建的目录都会把属主改成当前用户sudo chown -R 用户名:用户名 /data这个操作看似简单却避免了后续容器写入文件时出现一串 Permission denied。Docker 容器里的进程默认以 root 或特定 UID 运行如果宿主目录权限不对容器内无法写入服务就会起不来。3. Docker 部署核心服务相册备份、文件管理、媒体播放3.1 Docker 环境的初始化很多人装了 Docker 就直接开始跑容器结果磁盘空间越用越少日志把根目录写满。我先做两件事。一是安装 Docker 和 Docker Compose 插件二是修改 Docker 日志策略。Debian 下可以通过官方脚本安装 Docker或者用包管理器安装 docker.io然后用apt install docker-compose-v2安装 Compose 插件。之后修改/etc/docker/daemon.json对日志大小做限制{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 } }这个配置会限制每个容器日志文件的大小。不设这个限制某些话痨的容器一年能写几十 GB 日志。改完配置文件后执行systemctl restart docker。3.2 相册备份Immich手机相册备份是我搭建这套系统最强的动机。对比了几个开源相册服务Immich 的功能最接近主流网盘的相册体验后台自动备份、按时间线浏览、人脸识别、搜索、相册共享都有。Immich 官方推荐用 Docker Compose 部署因为它需要三个服务配合server后端、PostgreSQL数据库、Redis缓存。完整的 docker-compose.yml 可以从官方仓库拿我这里只标注几个必须改的地方services: server: image: ghcr.io/immich-app/immich-server:release ports: - 2283:3001 volumes: - /data/immich:/usr/src/app/upload environment: - UPLOAD_LOCATION/usr/src/app/upload - IMMICH_VERSIONreleaseUPLOAD_LOCATION对应宿主机的/data/immich所有照片都会落到这个目录。我还会把 Immich 的数据库目录挂载到宿主机/data/docker/immich/postgres避免容器重建后数据库丢失。部署好后进入 Immich 后台创建管理员账号手机安装官方 APP扫描网页上的二维码完成设备绑定打开“自动备份”开关。之后拍的照片大概几分钟内就会自动上传到家里这台小主机。实测下来备份体验很稳唯独人脸识别索引在 N100 上会跑得比较慢首次建立索引可能需要几小时这个不影响正常使用。3.3 文件管理FileBrowser家里不止有手机照片还有各种电脑上导出来的文档、安装包、PDF。FileBrowser 是我见过最省心的网页版文件管理器只要给它一个目录就能在浏览器里上传、下载、重命名、分享文件。部署方式直接一条命令docker run -d \ --name filebrowser \ -v /data:/srv \ -p 8080:80 \ filebrowser/filebrowser把整个/data挂载进去方便在手机浏览器里管理所有云盘内容。打开网页后默认账号密码是admin / admin登录后第一件事就是改密码。FileBrowser 还支持只读分享链接比如把一个电影分享给朋友看这个链接是可以限时的适合临时共享。3.4 家庭媒体中心JellyfinJellyfin 负责把下载好的电影、剧集、音乐整理成一个像流媒体平台一样的界面。电脑、手机、电视上都安装对应的客户端就能直接播放。它的优势是可以自动刮削海报、简介、演员信息也能对不支持直接播放的格式做实时转码。部署时关键在于把目录挂对services: jellyfin: image: jellyfin/jellyfin:latest ports: - 8096:8096 volumes: - /data/media:/media:ro - /data/docker/jellyfin/config:/config - /data/docker/jellyfin/cache:/cache devices: - /dev/dri/renderD128:/dev/dri/renderD128 environment: - PUID1000 - PGID1000/media挂载成只读模式避免 Jellyfin 意外修改原文件。/dev/dri/renderD128是 Intel 核显硬件转码设备没有这个设备的话影片遇到不支持的编码只能走 CPU 转码N100 的 CPU 转码性能会明显不够用。首次配置时在后台开“硬件加速”选择 Video Acceleration API然后保存重启服务。3.5 容器运维的基本功容器跑久了空间和数据都会出问题。至少养成三个习惯定期执行docker system df查看磁盘占用定期执行docker system prune清理悬空镜像和停止的容器重要的容器都要用docker compose管理而不是一条条docker run跑否则后续想要修改配置会很难受。4. 远程访问只留下一条既方便又安全的路4.1 为什么我不直接做公网端口映射家庭宽带现在 IPv4 公网地址越来越稀缺很多用户拿到的是运营商 NAT 之后的共享地址路由器端口映射做了也没用。即使拿到了公网地址把 2283、8096、8080 这些端口直接暴露到公网风险也很明确这些服务默认不具备高强度的登录保护很容易被全网扫描工具盯上分分钟被人尝试爆破。我的原则是默认情况下不给任何服务直接开公网口。需要外网访问就通过专门的通道来解决。4.2 用异地组网工具搭建一条专用通道现在社区里常用的一类方案是异地组网工具安装客户端后把家里的服务器、手机、电脑等设备组成一个虚拟局域网设备之间通信走端到端加密隧道不需要公网 IP 也不用在路由器上开放端口。我在这台小主机上装了组网工具的客户端手机上也装了一个两台设备登录同一个账号后就能拿到同一个虚拟网段内的 IP。在外面打开手机客户端直接访问家里的 Immich 地址相册备份和取文件都能正常用。这个方案的优点很明显服务没有被公网“看见”只有加入这个虚拟网络的设备才能访问攻击面小了很多。部署过程只需要在服务的官网注册账号、下载客户端、登录、把设备绑定到同一个网络基本不需要接触命令行。如果在手机端遇到访问不稳的情况大概率是碎片路由或网络切换的问题优先检查客户端状态而不是盲改家里服务配置。4.3 没有组网工具时DDNS 与白名单的有限组合有人可能不想依赖第三方服务或者需要让家庭成员临时访问。这时可以考虑用 DDNS 绑定动态公网 IPv6 或公网 IPv4然后在路由器防火墙上设定来源 IP 白名单。比如只允许自己的手机当前所在网段访问某些端口其他来源一律拒绝。这个方案的前提是有公网条件没有就没办法。即便使用白名单也必须对暴露的服务做加固禁用默认账户、改用高强度密码、开启两步验证、给服务加上反向代理并启用 HTTPS、部署 Fail2ban 自动封禁尝试爆破的 IP。动手之前想清楚你的需求是“偶尔访问”还是“天天访问”。如果是“偶尔”异地组网的成本和安全性都远优于开放端口。4.4 服务端访问控制的实践清单在我这台机器上所有服务的访问控制最终落在三层第一层组网工具只允许设备加入虚拟网络后访问第二层防火墙默认只放行局域网网段访问其他网段全部拒绝第三层每个服务自身都开启登录密码Docker 容器不挂安全漏洞容器。对于 SSH我修改了默认端口只允许密钥登录并禁用了 root 登录。这些操作在新建用户之后马上配置不需要等出现事故再做。远程访问这件事少一个开放端口就少一分夜间被扫的风险。5. 前三个月踩坑实录数据差点丢光的几个教训5.1 Docker 根分区被容器日志和镜像层撑爆第一个大坑是 Docker 根分区被写满。系统盘是 256G我觉得已经绰绰有余了结果有一天 Docker 服务突然异常容器全部重启失败。检查后才发现/var/lib/docker占满了整个分区。原因是当时没有设置 daemon.json 日志上限几个高频写日志的容器日志文件体积暴涨。再加上反复重建容器旧镜像和悬空层都没清理累计起来把分区填满了。解决办法是上文提到的 daemon.json 日志限制以及定期执行docker system prune。处理完之后我用一条命令批量清理了要保留旧版本之外的所有悬空资源docker system prune -af现在每周日我会跑一次手动清理没有再出过问题。5.2 rsync 的 --delete 差点清空备份盘这是我最接近崩溃的一次。当时备份脚本里写了--delete它会在同步时删除目标目录中源目录不存在的文件。某天数据盘因为 UUID 变化没有自动挂载成功我手动执行备份脚本时源目录/data实际是空目录rsync 进入一个空源同步把备份盘上上千个文件全部清掉。时间点恰好发生在一次计划内的系统重启之后等发现备份盘空了距离最老的备份文件已经过去将近一周。最后靠 Btrfs 快照救回大部分文件但那几分钟的慌张我永远记得。现在我的备份脚本里加了双重保险先检查源目录里是否包含一个隐藏标记文件如果没有就中止任务同时检查源目录总大小如果低于前一天记录量的 50%也马上停下来报警。用--delete之前想清楚源目录是否真的完整。5.3 硬盘休眠导致视频播放卡死Jellyfin 平时用得很爽但某几次播放高清电影时进度条转了十几秒才出画面甚至直接卡死。排查日志发现机械硬盘在空闲状态下进入了节能休眠Jellyfin 读取文件时需要等盘片启动这个过程有时候长达十几秒。解决办法不是硬关休眠而是调整策略。我用smartctl看了一下硬盘负载再用hdparm -S把空闲休眠 time out 改长一些同时设置 Jellyfin 的目录扫描任务在凌晨执行让盘在需要时保持活跃。比较简单的做法是给机械盘的数据加入一个每小时一次的轻量读取让盘片不会完全停转但这样也会增加一些功耗和磁盘寿命磨损需要自己权衡。我最后用了一种更优雅的折中方案把媒体文件放在机械盘把 Jellyfin 的元数据和缓存放到 SSD 上这样打开首页、读取海报、分析媒体库都不需要唤醒机械盘只有真正播放时才读一次盘。体验改善非常明显。5.4 容器权限错乱PUID/PGID 不统一Docker 容器内部有自己的一套 UID/GID 体系默认是 root 用户但很多镜像会以普通用户运行比如 PUID1000、PGID1000。我当时跑 Jellyfin 和 Immich一个指定了 PUID一个没指定最后宿主目录的属主变得混乱备份时出现大量权限报错容器之间共享文件也互相读不了。教训是所有需要持久化写入宿主目录的容器都要在 docker-compose 里显式指定 PUID 和 PGID并且统一用同一个值。我用的是 1000对应日常管理用户。这样容器内写出来的文件在宿主机操作时也是同一个用户不会出现“这个目录只能容器里删宿主机动不了”的怪问题。5.5 数据库备份不能简单拷贝文件Immich 使用 PostgreSQL 保存元数据我一开始的备份方式是从容器里把整个 postgres 数据目录复制到备份盘。直到有一次磁盘空间异常我尝试从备份恢复 Immich数据库服务怎么都起不来错误信息提示数据目录损坏。原因很简单PostgreSQL 运行时直接拷贝数据文件得到的可能是不一致状态必须通过它自己的导出机制来备份。现在我会用 cron 定时执行pg_dump把数据库内容导出成 SQL 文件再把这个文件同步到备份盘。具体操作是进入容器执行导出docker exec 容器名 pg_dump -U postgres -d immich /backup/immich_db_$(date %Y%m%d).sql这个习惯后来救了我一次。一个应用升级失败导致数据库无法正常启动用 SQL 文件导入后立刻恢复损失几乎为零。5.6 恢复演练比备份本身更重要前面几个坑让我意识到一个问题备份脚本不是写完就一劳永逸。收藏夹里存着再完美的备份计划如果没有定期验证恢复都无法知道它到底能不能用。我每月做一次“灾难恢复演练”从备份盘提取一个数据库 SQL 文件把它导入到临时容器从 Btrfs 快照里恢复一个测试目录检查备份盘上的文件时间戳和大小是否正常。第一次演练就发现备份脚本因为 cron 环境变量问题已经两周没有执行成功。如果在真正丢数据的时刻才发现就已经太晚了。搭建家庭私有云盘这件事做到“能跑起来”很容易但做到“关键时候能救命”不容易。我现在的原则是所有自动化的东西都必须有一个能验证的入口宁可多花点时间做一次完整的恢复测试也不要等到数据丢了再拍大腿。这台小主机现在已经在家里稳定运行了大半年手机相册自动备份再也没让我操心过客厅的电视、父母的平板、我的笔记本都直接从这台机器上读内容。长远规划也许还会加一块更大容量的硬盘但如果从零开始再选一次我仍会坚持三步走先定需求再选硬件用开源系统掌控自己的数据把备份和恢复演练当作和备份本身同等重要的事情来对待。