打开网易新闻 查看精彩图片

Immich 官方文档写着"需要 Docker 和 Linux 服务器"。一位开发者花了三周,把它塞进了没 root 的安卓手机——总成本 0 元,云端依赖 0 个。

这事听起来像把 SUV 发动机装进摩托车,还要让它跑川藏线。但代码已经开源,步骤可复制。我们拆解了全部技术路径。

Immich 是什么,为什么非要本地跑

Immich 是什么,为什么非要本地跑

Immich 是个自托管的照片管理平台,功能对标 Google Photos:自动备份、人脸识别、自然语言搜图、地图时间线。区别在于数据完全本地,不上传任何云端。

官方部署方式是 Docker Compose 一键拉起:PostgreSQL 管数据,Redis 做缓存,Node.js 跑主服务,Python 处理 AI 识别。四件套,缺一不可。

开发者「mertalev」的痛点很具体:他有 3 万多张照片,不想续费云盘,也不信任厂商的 AI 扫描。但手头只有一部旧安卓机——骁龙 865,8GB 内存,256GB 存储。

买树莓派?加硬盘?他的解法更极端:直接让手机自己当服务器

安卓不是 Linux,这是第一道墙

安卓不是 Linux,这是第一道墙

Termux 是安卓上的终端模拟器,能跑 Linux 命令,但底层是 Bionic libc 而非 glibc。这个差异导致 90% 的 Linux 软件无法直接运行。

mertalev 列了四个硬骨头:

PostgreSQL 需要 glibc 和内核特性,Termux 原生不支持;Node.js 最新版在 Bionic 上静默崩溃;Python 的 AI 库(ONNX Runtime、InsightFace)没有安卓预编译包;Sharp 等图像处理库依赖 glibc 二进制。

他的架构图很清晰:PostgreSQL 塞进 proot Debian 容器(模拟完整 Linux 环境),Redis、Node.js、Python 服务全部原生运行在 Termux。

数据流这样走:proot 里的 PostgreSQL 监听 localhost:5432,Termux 原生服务通过同一端口读写。容器内外靠本地网络打通,没有性能损耗。

Node.js 的"身份伪装"

Node.js 的"身份伪装"

Immich 服务端基于 Node.js,依赖大量原生模块。这些模块安装时会检测平台,自动下载对应预编译二进制。

打开网易新闻 查看精彩图片

检测到安卓?直接报错或下载错误版本。mertalev 的解法是指定环境变量强行"骗过"构建系统:

npm_config_platform=linux

npm_config_arch=arm64

npm_config_libc=glibc

三行配置让 npm 以为自己在给 Linux ARM64 服务器装依赖,顺利拉取 linux-arm64 预编译包。但 Sharp 图像库不吃这套——它硬编码了平台检测逻辑。

Sharp 的处理更粗暴:强制使用 Termux 系统自带的 libvips,跳过预编译二进制。环境变量 SHARP_FORCE_GLOBAL_LIBVIPS=1 开启后,Sharp 从源码重新编译,链接到 Bionic 环境。

bcrypt 密码哈希库同理,全量重编。构建耗时从分钟级拉到小时级,但换来了可运行性。

Python AI 库的"荒野求生"

Python AI 库的"荒野求生"

Immich 的 ML 服务依赖 ONNX Runtime(神经网络推理引擎)和 InsightFace(人脸识别)。PyPI 上这两个库完全没有 android-aarch64 的 wheel 包。

mertalev 用 uv(Rust 写的 Python 包管理器)指定交叉编译目标:

uv sync --python-platform manylinux_2_28_aarch64

这行命令让构建系统以为目标平台是标准 Linux ARM64,生成兼容的二进制。但实际运行环境是 Bionic,需要手动处理符号链接和动态库路径。

watchfiles(文件监控库)被直接移除——它依赖 maturin(Rust 构建工具),而 maturin 在 Android 上完全无法工作。功能损失:照片目录的实时热更新,改为手动重启服务触发扫描。

其他补丁包括:Node 堆内存限制提到 6GB 防止构建时 OOM;临时目录改到 $HOME/tmp 规避安卓 /tmp 只读限制;强制指定 bash 作为脚本解释器(Termux 默认 sh 不兼容部分安装脚本)。

PostgreSQL 的"容器囚笼"

PostgreSQL 的"容器囚笼"

打开网易新闻 查看精彩图片

整个架构里最别扭的是 PostgreSQL。它必须跑在 proot Debian 容器里,因为需要 glibc 和完整的 Linux 系统调用。

proot 是用户态 chroot,不需要 root 权限,但性能损耗极低(接近原生)。容器内安装 PostgreSQL 17 和 VectorChord 扩展(Immich 依赖的向量检索插件)。

坑出现在权限:PostgreSQL 默认拒绝以 root 启动,而 proot 容器默认就是 root 环境。mertalev 的 workaround 是在容器内创建 postgres 用户,通过脚本切换身份启动服务。

启动流程变成:Termux 执行 proot 命令 → 进入 Debian 环境 → 切换到 postgres 用户 → 启动 PostgreSQL → 暴露 5432 端口给宿主机。

容器内外通过 localhost 通信,延迟低于 1ms。对照片管理场景完全可接受。

跑起来之后

跑起来之后

完整启动后,手机同时运行:proot Debian(PostgreSQL)、Redis 服务器、Node.js 主服务(2283 端口)、Python ML 服务(3003 端口)。

内存占用:空闲时约 2.5GB,照片导入和 AI 索引时冲到 6GB 以上。骁龙 865 的 AI 算力被 ONNX Runtime 调用,人脸识别速度约 5-10 张/秒。

mertalev 的 3 万张照片完成首次索引耗时约 6 小时,期间手机发热明显但稳定运行。后续增量导入几乎无感知。

存储方面:原始照片 180GB,PostgreSQL 数据库 15GB,缩略图和向量索引 25GB。256GB 手机刚好够用。

谁需要这个

谁需要这个

这个方案不是给普通用户的。配置过程涉及环境变量调试、源码编译、容器网络排错,需要能读懂 Rust/Python/Node 报错信息的能力。

但它的存在证明了几件事:安卓手机的硬件性能早已过剩,限制我们的是软件生态;Termux 和 proot 的组合能撑起完整的服务端 workload;照片数据的"本地优先"技术路径已经打通。

mertalev 在 GitHub 的 README 里写:「这不是生产级方案,但它是我的方案。」

如果你也有一部闲置安卓机,和不愿上云的照片——这个 0 元服务器,会是你想要的答案吗?