Immich 开发环境搭建指南:用 mise 与 Docker Compose 一键启动 Server、Web 与 ML 全栈开发
本文基于 Immich 官方开发者文档 setup.md 展开,系统讲解如何在本机搭建 Immich 的完整开发环境:包括 mise 工具链安装、docker-compose.dev.yml 多服务开发栈的启动、Web 前端对接远程后端、@immich/ui 本地联调、Flutter 移动端调试与 iOS 签名覆盖、国际化翻译生成,以及 IDE 的 lint/format 配置。读完本文后,你能够从零复制出一个支持热重载的 Immich 开发实例,并了解每个命令背后的任务定义与 compose 文件细节。
一、开发环境概览:五个核心服务
进入代码之前,官方提醒先阅读 CONTRIBUTING.md。此外,如果你计划贡献某个功能,文档建议在 Discord 的 #contributing 频道提前知会维护者,以确认功能可被接受、获得实现建议并避免重复劳动。
开发环境由以下服务组成,各服务的详细说明见其各自的 README:
| 服务 | 仓库位置 | 说明 |
|---|---|---|
| Server | server | NestJS 后端,负责 API、任务队列与管理功能 |
| Web app | web | SvelteKit 前端应用 |
| Machine learning | machine-learning | Python FastAPI 机器学习服务 |
| Redis | — | 缓存与队列后端 |
| PostgreSQL | — | 开发数据库,暴露 5432 端口,可用任意数据库客户端直接访问 |
所有服务都打包在一条 Docker Compose 命令中启动。对应的开发用编排文件是 docker/docker-compose.dev.yml,从源码结构看它定义了 6 个容器:
immich_init:基于server/Dockerfile.dev的dev构建目标,容器启动后执行mise install并写入/tmp/init-complete标记,健康检查通过后才放行其他服务(retries: 300,start_period: 300s),相当于整个开发栈的初始化前置任务;immich_server:挂载整个仓库..:/usr/src/app,把${UPLOAD_LOCATION}/photos映射为容器内/data,并额外挂载 packages/plugin-core,对外暴露9230、9231、2283三个开发端口;immich_web:SvelteKit 开发服务器,对外暴露3000(Web 界面)与24678端口;immich_machine_learning:从 machine-learning/Dockerfile 构建,默认DEVICE=cpu(可改armnn、cuda、rocm、openvino、openvino-wsl、rknn之一),将../machine-learning/immich_ml源码目录直接挂载进容器以支持热重载,暴露3003端口;redis(valkey 镜像)与database(PostgreSQL 镜像,shm_size: 128mb,数据库数据落在${UPLOAD_LOCATION}/postgres)。
其中 node_modules、.svelte-kit、构建缓存等都以命名卷形式挂入容器(server_node_modules、web_node_modules、sveltekit 等),避免把宿主机的权限问题带进容器。
mise:项目工具与任务管理中枢
整个项目统一使用 mise 管理工具版本并执行任务。安装 mise 后,在仓库根目录执行:
mise trust
mise install
即可装齐全部依赖工具。从根目录 mise.toml 可以确认当前锁定的工具版本:Node 24.15.0、pnpm 11.22.0、Java 21.0.2、oazapfts 7.5.0、openapi-generator-cli 2.40.1,以及跨平台的 jellyfin-ffmpeg 7.1.3-6。[monorepo] 配置声明了 server、web、mobile、e2e、docs、machine-learning 等 config_roots,因此每个子目录各自还有 mise.toml 定义本服务任务。
任务从仓库根目录用 //namespace:task 语法执行,例如 mise //server:lint;用 mise tasks ls --all 列出所有可用任务。根目录 mise.toml 中与开发启动直接相关的任务包括:
//:dev:先依赖//:plugins(构建 SDK、plugin-sdk、plugin-core),再在docker/目录下执行docker compose -f ./docker-compose.dev.yml up --remove-orphans(COMPOSE_BAKE=true,交互式运行),结束后自动执行//:dev-down;//:dev-update:在dev基础上追加--build -V,强制重建镜像并清理容器;//:dev-down:docker compose -f ./docker-compose.dev.yml down --remove-orphans。
二、启动 Server 与 Web 开发栈
按官方步骤操作:
- 克隆项目仓库;
- 复制环境变量模板:
cp docker/example.env docker/.env; - 编辑
docker/.env,为必填变量UPLOAD_LOCATION提供值; - 安装依赖:
mise x -- pnpm i; - 在仓库根目录执行:
mise dev
- 在浏览器访问 http://localhost:3000,或用移动端 App 连接该实例。
所有服务启动时都启用热重载,形成快速反馈回路。Web 可通过 http://your-machine-ip:3000 或 http://localhost:3000 访问;移动端 App 则通过 http://your-machine-ip:3000 连接开发后端。
注意事项:web 开发容器以 uid 1000 运行,如果该 uid 对挂载的卷没有读写权限,可能会遇到错误。
关于 .env 文件,docker/example.env 给出了完整模板:UPLOAD_LOCATION=./library(上传文件存储位置)、DB_DATA_LOCATION=./postgres(数据库文件位置,不支持网络共享)、可选的 TZ 时区、IMMICH_VERSION(可固定到具体版本如 v2.1.0,模板中默认为 v3)、DB_PASSWORD(建议改为随机密码,仅限 A-Za-z0-9),以及无需修改的 DB_USERNAME=postgres 与 DB_DATABASE_NAME=immich。
仅开发 Web:连接远程后端
如果只想做 Web 开发并连接一个已存在的远程后端(不需要本地起 Server),在仓库根目录执行:
IMMICH_SERVER_URL=https://demo.immich.app/ mise //web:start
这一步会同时安装所有依赖(含 SDK)并启动开发服务器。若专门连接官方托管的 demo 服务器,可用简写:
mise //web:start-demo
这两个命令对应 web/mise.toml 中的任务链:start 依次执行 :install(pnpm install --filter immich-web --frozen-lockfile)、//:sdk:install、//:sdk:build,最后 pnpm run dev;start-demo 则是在 :start 之前注入环境变量 IMMICH_SERVER_URL=https://demo.immich.app。
Windows PowerShell 中可能需要单独设置环境变量:
$env:IMMICH_SERVER_URL = "https://demo.immich.app/"
mise //web:start
从 web/vite.config.ts 可以看到该变量如何生效:upstream 取 process.env.IMMICH_SERVER_URL,缺省为容器内服务发现地址 http://immich-server:2283/,并将 /api、/.well-known/immich、/custom.css 三类请求代理到该上游(ws: true 保证 WebSocket 也走代理)。这就是"Web-only 开发"模式下前端无需本地后端即可联调的底层机制。
本地联调 @immich/ui 设计系统
Web 端大量 UI 组件来自 @immich/ui 包(在 web/package.json 中以 ^0.86.0 引入,被 web/src/routes/+layout.svelte、web/src/lib/utils.ts 等上百处文件使用)。若要看到本地对 @immich/ui 的修改在 Immich 中生效,官方给出的流程是:
- 将
@immich/ui仓库安装为immich/的兄弟目录,例如/home/user/immich与/home/user/ui; - 在
@immich/ui项目中执行pnpm run build构建; - 在 docker/docker-compose.dev.yml 的 web 服务中取消注释对应卷挂载
../../ui:/usr/src/ui(该注释行位于immich-app-base基础 profile 的 volumes 中); - 在 web/vite.config.ts 中取消注释对应 alias(
'@immich/ui': path.resolve(..., '../../ui/packages/ui')),让模块解析指向本地源码; - 在 web/src/app.css 中取消注释
@import '../../../ui/packages/ui/dist/theme/default.css';,同时注释掉@import '@immich/ui/theme/default.css';,让样式也来自本地构建产物; - 通过
mise dev启动整个栈; - 在
@immich/ui中每次改动后重新构建(pnpm run build)。
三、移动端(Flutter)开发
基本启动步骤
- 执行
mise //mobile:install安装 Flutter 依赖; - 执行
mise //mobile:translation生成翻译文件; - 进入
mobile/目录,执行flutter run启动应用。
从 mobile/mise.toml 可确认工具链细节:Flutter 版本锁定为 3.47.1(安装后自动执行 ios/scripts/xcode_flutter_patch.sh 补丁脚本);install 任务先依赖 //:open-api-dart(从 OpenAPI 规范重新生成 Dart SDK),再执行 flutter pub get,并在 macOS 上自动 cd ios && pod install。App 启动时会询问要连接哪个后端:如果不需要改服务端代码或上传照片,可直接使用 demo 后端(https://demo.immich.app/);否则按上文自行启动服务。
iOS 代码签名
Immich 的 Apple Team ID 与 Bundle ID 定义在 mobile/ios/Signing.xcconfig 中,这些环境变量被各 target 与 scheme 复用,避免贡献者重复修改。本地开发提供了覆盖机制:创建 mobile/ios/Signing.local.xcconfig(该文件已被 gitignore),填入自己的签名值:
IMMICH_TEAM_ID = ABCDE12345
IMMICH_BUNDLE_ID_PROD = com.customuniqueid.immich
IMMICH_BUNDLE_ID_DEV = com.customuniqueid.immichdev
IMMICH_GROUP_ID = group.com.customuniqueid.immich
添加新的翻译文案
在仓库根目录的 i18n/en.json 中加入新的 key-value 对,然后执行:
mise //mobile:translation
对应 mobile/mise.toml 中 codegen:translation 任务:它依赖 //:i18n:format-fix(规范化 JSON 格式)、i18n:loader(dart run easy_localization:generate -S ../i18n 生成 loader)和 i18n:keys(dart run bin/generate_keys.dart 生成类型安全的翻译 key)。因此新增文案后必须跑这一步,才能在 Dart 代码中引用到新 key。
UI 组件与 Widget 预览
共享设计系统组件(按钮、输入框、表单等)位于 immich_ui 包 mobile/packages/ui 下:组件定义在 lib/src/components/(如 form.dart、text_input.dart、password_input.dart、icon_button.dart 等),并有与之对应的 lib/src/previews/ 预览文件。
要单独查看某个组件(支持明暗主题切换与热重载),启动 Flutter 的 Widget Previewer:
cd mobile/packages/ui
flutter widget-preview start
在安装了 Flutter 插件的 VS Code 或 Android Studio 中,打开侧边栏的 Flutter Widget Preview 标签页即可自动启动预览器。
四、IDE 配置
Lint / Format 扩展
在 IDE 中正确配置格式化器可以自动在保存时格式化代码,并即时反馈 lint 问题,显著提升开发体验。
Dart Code Metrics
移动端使用 DCM(Dart Code Metrics)做 lint 与度量计算,配置方式参见其官方 Getting Started 文档。注意:激活 DCM 许可证不是必需的——从 mobile/mise.toml 可以看到 analyze 任务由 dart analyze --fatal-infos 与 dcm analyze lib --fatal-style --fatal-warnings 两部分组成,dcm 工具本身由 mise 从 GitHub 资产(各平台 zip 包)拉取,1.39.1 版本。
VS Code
推荐安装 Flutter、DCM、Prettier、ESLint、Svelte 等扩展,它们已列在 .vscode/extensions.json 中,会作为工作区推荐出现。该文件实际推荐了 14 个扩展,除上述核心五件外还包括 Tailwind CSS、Playwright、Vitest Explorer、EditorConfig、Shell Format、ShellCheck、yamlfmt,以及预览阶段的 typescriptteam.native-preview。
工作区设置见 .vscode/settings.json,与官方文档一致的核心内容如下:
{
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"[dart]": {
"editor.defaultFormatter": "Dart-Code.dart-code",
"editor.formatOnSave": true,
"editor.selectionHighlight": false,
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.suggestSelection": "first",
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": "off"
},
"[javascript]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"[svelte]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "svelte.svelte-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"[typescript]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"cSpell.words": ["immich"],
"editor.formatOnSave": true,
"eslint.validate": ["javascript", "svelte"],
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
"*.ts": "${capture}.spec.ts,${capture}.mock.ts"
},
"svelte.enable-ts-plugin": true,
"typescript.preferences.importModuleSpecifier": "non-relative"
}
几个值得注意的细节:文件嵌套规则会把 .g.dart/.drift.dart/.gr.dart 等生成产物折叠到源文件下,把 .spec.ts/.mock.ts 折叠到对应模块下;typescript.preferences.importModuleSpecifier 设为 non-relative 配合 web 端的非相对路径导入约定。
五、小结与延伸阅读
整套开发流程可以概括为:mise trust && mise install 装齐工具链 → cp docker/example.env docker/.env 并配置 UPLOAD_LOCATION → mise dev 一条命令拉起带热重载的六容器开发栈 → 浏览器访问 http://localhost:3000。仅改 Web 时可用 IMMICH_SERVER_URL 指向远程后端;改移动端时通过 mise //mobile:install 与 mise //mobile:translation 生成 SDK 与翻译产物,iOS 签名则用 Signing.local.xcconfig 覆盖。
排错时可配合开发者文档中的 troubleshooting、architecture 与 testing 继续深入;数据库结构变更参见 database-migrations,多语言协作流程参见 translations。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00