immich-app/immich
High performance self-hosted photo and video management solution.
Mixed signals — read the receipts
- ✓Last commit today
- ✓5 active contributors
- ✓Distributed ownership (top contributor 35%)
- ✓AGPL-3.0 licensed
- ✓CI configured
- ✓Tests present
- ⚠Small team — 5 top contributors
- ⚠AGPL-3.0 is copyleft — check downstream compatibility
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Embed this verdict
[](https://repopilot.app/r/immich-app/immich)Paste into your README — the badge live-updates from the latest cached analysis.
Onboarding doc
Onboarding: immich-app/immich
Generated by RepoPilot · 2026-05-05 · Source
Verdict
WAIT — Mixed signals — read the receipts
- Last commit today
- 5 active contributors
- Distributed ownership (top contributor 35%)
- AGPL-3.0 licensed
- CI configured
- Tests present
- ⚠ Small team — 5 top contributors
- ⚠ AGPL-3.0 is copyleft — check downstream compatibility
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
TL;DR
Immich is a self-hosted photo and video management platform that replicates Google Photos-style functionality on your own infrastructure. It provides automatic mobile backup, facial recognition, smart search via machine learning (CLIP embeddings), timeline browsing, albums, and sharing — all without cloud vendor lock-in. The server is TypeScript/NestJS, the web frontend is SvelteKit, and the mobile clients are Flutter/Dart for iOS and Android. This is a monorepo: the NestJS server lives in server/, the SvelteKit web app in web/, the Flutter mobile app in mobile/, a standalone CLI in cli/, and a Python machine learning service in machine-learning/. Shared TypeScript SDK/API types are generated from OpenAPI specs and consumed by web/ and cli/. DevContainer configs in .devcontainer/server/ and .devcontainer/mobile/ define the local development environments.
Who it's for
Privacy-conscious individuals and homelab/self-hosting enthusiasts who want a full-featured Google Photos replacement under their own control. Contributors are typically TypeScript/NestJS backend engineers, Svelte frontend developers, and Flutter/Dart mobile developers who want to extend or fix specific photo management workflows.
Maturity & risk
Immich is one of the fastest-growing self-hosted projects on GitHub (50k+ stars), has been in active development since 2022, and has a comprehensive CI pipeline under .github/workflows/ covering Docker builds, mobile builds, OpenAPI checks, CodeQL security analysis, and automated tests. It is production-ready for personal use with the strong caveat from its own README to always maintain a 3-2-1 backup — the project is actively iterating and not yet API-stable.
The project moves fast with frequent breaking changes between versions (upgrade notes are mandatory reading). It is a complex multi-service stack (Postgres + pgvecto.rs, Redis, machine learning service, main server, microservices worker) meaning local dev requires Docker Compose and several cooperating containers. Single-maintainer risk is partially mitigated by an active contributor community, but the core architecture decisions are centralized.
Active areas of work
Active CI workflows include automated OpenAPI spec checks (.github/workflows/check-openapi.yml), SDK generation (.github/workflows/sdk.yml), and mobile builds (.github/workflows/build-mobile.yml), suggesting ongoing API evolution and mobile feature work. The prepare-release.yml and merge-translations.yml workflows indicate regular release cadence and active internationalization efforts across 18+ languages visible in the README.
Get running
git clone https://github.com/immich-app/immich.git && cd immich
Use the devcontainer (recommended) or manually:
cd server && npm install
For full stack local dev, use Docker Compose:
cp docker/example.env docker/.env docker compose -f docker/docker-compose.yml up --build
Web frontend dev server:
cd web && npm install && npm run dev
The repo uses pnpm at the workspace level (see .pnpmfile.cjs):
pnpm install
Daily commands:
Full stack (recommended):
cd docker && docker compose up --build
Web only (against existing backend):
cd web && npm run dev
Server only (in devcontainer):
bash .devcontainer/server/container-start-backend.sh
Frontend only:
bash .devcontainer/server/container-start-frontend.sh
Map of the codebase
- .devcontainer/server/devcontainer.json: Defines the recommended VS Code devcontainer for server-side development including all required service dependencies.
- .github/workflows/test.yml: Central test workflow — shows exactly how unit and integration tests are run across server, web, and mobile.
- .github/workflows/check-openapi.yml: Enforces that the OpenAPI spec stays in sync with NestJS controller decorators — any API change must pass this.
- .github/workflows/docker.yml: Defines how production Docker images are built and pushed for the server, web, and ML service.
- .github/workflows/build-mobile.yml: Flutter/Dart mobile build pipeline for both iOS and Android targets.
- .github/mise.toml: Pins exact tool versions (Node, pnpm, Flutter, etc.) used in CI — must match local dev environment to avoid surprises.
- .pnpmfile.cjs: pnpm workspace hook — any dependency resolution quirks or overrides for the monorepo live here.
- docker/docker-compose.yml: Canonical multi-service compose file defining postgres, redis, immich-server, immich-microservices, and immich-machine-learning containers.
How to make changes
Backend API changes: start in server/src/ — controllers define routes, services contain business logic, entities map to DB tables. New REST endpoints require updating the OpenAPI spec (run the check-openapi workflow to validate). Web UI changes: web/src/ with Svelte components. New mobile screens: mobile/lib/ in Dart. Shared API types are in the generated SDK — regenerate with the sdk workflow after backend changes. Translations: add keys in the i18n files referenced by merge-translations.yml.
Traps & gotchas
- PostgreSQL must have the
pgvecto.rsextension installed — the standard postgres image won't work; useghcr.io/tensorchord/pgvecto-rs. 2) The ML service requires significant RAM (1-2GB+) for CLIP models; it will silently fail on memory-constrained hosts. 3) The OpenAPI spec is auto-generated and committed — forgetting to regenerate it after a controller change will fail thecheck-openapiCI gate. 4) pnpm is the package manager (.pnpmfile.cjspresent) — using npm at the workspace root will produce incorrect lockfiles. 5) Mobile devcontainer requires a separate compose override file at.devcontainer/mobile/container-compose-overrides.yml.
Concepts to learn
- CLIP (Contrastive Language-Image Pretraining) — Immich's smart search uses CLIP embeddings to match natural language queries to photos without manual tagging — understanding this explains why the ML service is a hard dependency.
- pgvecto.rs / Vector Similarity Search — Photo similarity, semantic search, and face clustering are implemented as ANN (approximate nearest neighbor) queries against CLIP embedding vectors stored in PostgreSQL via this extension.
- BullMQ Job Queues — Asset processing (thumbnail generation, EXIF extraction, ML inference) is done asynchronously through BullMQ Redis-backed queues in the microservices worker — understanding this is essential for debugging processing pipelines.
- OpenAPI Code Generation — The TypeScript SDK and Dart client are fully generated from the NestJS OpenAPI spec — any backend API change requires regenerating clients, and the CI enforces spec consistency.
- Devcontainers (VS Code) — The repo ships first-class devcontainer configurations that spin up all required services via Docker Compose — skipping this and developing natively is a common source of environment mismatch bugs.
- Perceptual Hashing (dHash/pHash) — Immich uses perceptual hashing for duplicate asset detection, which is fundamentally different from cryptographic hashing — two visually identical photos with different metadata will match.
- ArcFace / Face Recognition Embeddings — Face clustering in Immich uses ArcFace-style embedding models to group photos by person — this drives the 'People' feature and requires the ML service to be running.
Related repos
photoprism/photoprism— Direct alternative self-hosted photo management solution using Go + TensorFlow, solving the same Google Photos replacement problem.LibrePhotos/librephotos— Another self-hosted Google Photos alternative using Django/Python backend, a close architectural peer.immich-app/immich-go— Official companion CLI tool written in Go for bulk-uploading assets to an Immich instance from Google Takeout exports.tensorchord/pgvecto.rs— The required PostgreSQL extension for vector similarity search that powers Immich's smart search and face clustering features.seriousm4x/UpSnap— Popular homelab self-hosting project in the same ecosystem, commonly deployed alongside Immich by the same user base.
PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add Prettier pre-commit hook via lint-staged to enforce formatting before commits
The repo has Prettier configured in .github/package.json with format and format:fix scripts, and a .github/workflows/fix-format.yml workflow that auto-fixes formatting. However, there is no lint-staged or husky pre-commit hook setup visible in the file structure. Adding a pre-commit hook would catch formatting issues locally before they even reach CI, reducing noisy fix-format workflow runs and improving developer experience. This is especially valuable for a large multi-language repo like immich with contributors from many backgrounds.
- [ ] Add husky and lint-staged as devDependencies in the root package.json (or .github/package.json)
- [ ] Create a .husky/pre-commit script that runs lint-staged
- [ ] Configure lint-staged in package.json or .lintstagedrc to run
prettier --write --cacheon staged files matching the patterns in .github/.prettierignore - [ ] Test the hook locally by staging a poorly formatted file and attempting a commit
- [ ] Update CONTRIBUTING.md to mention the pre-commit hook and how to skip it with --no-verify if needed
Add GitHub Actions workflow for CLI package publishing dry-run and test on PRs
The repo has a cli/ directory with its own Dockerfile, .npmignore, and .nvmrc, and there is a .github/workflows/cli.yml workflow file. However, there is no evidence of a PR-level dry-run publish check for the CLI package. Adding a workflow step that runs npm publish --dry-run on PRs touching cli/ would catch packaging issues (missing files, wrong entry points, broken bin/immich script) before a real release. This directly protects the cli/bin/immich entrypoint and cli/.npmignore correctness.
- [ ] Edit .github/workflows/cli.yml to add a job triggered on pull_request when paths include 'cli/**'
- [ ] Add a step that sets up Node using the version in cli/.nvmrc
- [ ] Add a step that runs
npm installandnpm run buildinside the cli/ directory - [ ] Add a step that runs
npm publish --dry-runto validate the package tarball contents against cli/.npmignore - [ ] Add a step that smoke-tests the cli/bin/immich entrypoint by invoking it with --help and asserting a zero exit code
- [ ] Ensure the job posts a summary comment or annotation if the dry-run fails
Add devcontainer feature documentation and quickstart guide for the server and mobile devcontainers
The repo has two distinct devcontainer configurations: .devcontainer/server/ and .devcontainer/mobile/, each with their own compose overrides and startup scripts (container-start-backend.sh, container-start-frontend.sh, container-common.sh). CONTRIBUTING.md exists but the file listing shows no dedicated devcontainer setup guide. New contributors frequently struggle with local environment setup for full-stack apps. Adding a focused devcontainer quickstart doc that explains what each script does, which devcontainer to choose for which task, and how the compose overrides interact, would significantly lower the barrier to first contribution.
- [ ] Create docs/contributing/devcontainer-setup.md (or add a section to CONTRIBUTING.md) explaining the two devcontainer profiles: .devcontainer/server/ and .devcontainer/mobile/
- [ ] Document the purpose of .devcontainer/server/container-common.sh, container-start-backend.sh, and container-start-frontend.sh with example outputs
- [ ] Explain how .devcontainer/server/container-compose-overrides.yml and .devcontainer/mobile/container-compose-overrides.yml differ from the main docker-compose setup
- [ ] Add a step-by-step 'First Run' section: prerequisites, opening in VS Code, selecting the correct devcontainer, verifying the app is running
- [ ] Reference .vscode/extensions.json and .vscode/launch.json
Good first issues
- Add missing test coverage for the CLI package —
.github/workflows/cli.ymlexists but the cli/ directory likely has limited unit tests compared to the server. 2) The.github/workflows/static_analysis.ymlworkflow could be extended with stricter TypeScriptnoUncheckedIndexedAccesschecks in the web/ SvelteKit package. 3) Several README translation files underreadme_i18n/may be outdated relative to the English README — auditing and updating one language's README is a low-risk, high-value contribution.
Top contributors
- @renovate[bot] — 17 commits
- @shenlong-tanwen — 10 commits
- @danieldietzler — 8 commits
- @timonrieger — 7 commits
- @mertalev — 6 commits
Recent commits
0058df7— fix(mobile): show lens info without lens name (#28234) (benbeckford)97100a4— refactor: app metadata (#28113) (shenlong-tanwen)af39384— chore: better contrast for highlighted button on control bar (#28217) (alextran1502)01712cf— fix(server): av typing (#28223) (mertalev)2015f95— fix(web): correct timeline yesterday label across month boundaries (#28183) (michelheusschen)d4f29ab— fix(server): validate duplicate group ownership before dismissal (#28221) (timonrieger)3decc86— refactor(server)!: structured validation error responses (#28204) (timonrieger)eca0e60— fix: librknnrt permissions in machine-learning (#28216) (DavidTheFighter)8cff588— fix(ml): respect time zone for logs in cuda container (#28155) (AyaanMAG)3d320d9— fix(web): fix shared link /s/photos.* navigation after password login (#27788) (meesfrensel)
Security observations
- Medium · Potential Hardcoded Secrets in .env File —
deployment/.env. The repository contains a deployment/.env file tracked in the repository structure. Environment files often contain sensitive credentials such as database passwords, API keys, JWT secrets, or encryption keys. If this file contains real credentials and is committed to version control, it could expose sensitive information to anyone with repository access. Fix: Ensure deployment/.env is listed in .gitignore and never committed with real credentials. Use environment-specific secret management solutions (e.g., HashiCorp Vault, AWS Secrets Manager, GitHub Secrets). Provide a .env.example file with placeholder values only. - Medium · Dependency Version Pinning - Prettier Using Caret Range —
.github/package.json. The package.json in .github uses 'prettier': '^3.7.4', which allows automatic minor and patch version upgrades. While prettier is a dev dependency and primarily a formatting tool, the use of caret (^) ranges in dependencies can introduce unexpected behavior or, in a supply chain attack scenario, malicious code if a package registry is compromised. Fix: Pin dependency versions exactly (e.g., '3.7.4' without the caret) and use a lock file (pnpm-lock.yaml) to ensure reproducible builds. Consider using tools like Renovate or Dependabot to manage controlled, reviewed dependency updates. - Medium · Container Start Scripts May Execute Arbitrary Commands —
.devcontainer/server/container-common.sh, .devcontainer/server/container-start-backend.sh, .devcontainer/server/container-start-frontend.sh. The devcontainer shell scripts (container-common.sh, container-start-backend.sh, container-start-frontend.sh) are executed within container environments. If these scripts source external input or use variables without proper sanitization, they could be vectors for command injection in development environments. Additionally, dev container configurations can sometimes mirror production setups. Fix: Review all shell scripts for unquoted variables, use of eval, or sourcing from untrusted locations. Apply shellcheck linting to all shell scripts. Ensure devcontainer configurations are isolated from production infrastructure. - Medium · CLI Binary Without Integrity Verification —
cli/bin/immich. The CLI component (cli/bin/immich) is distributed as a binary. Without checksum/signature verification mechanisms in place, users downloading or using this binary cannot verify its integrity. This could be exploited in a supply chain attack scenario. Fix: Provide cryptographic signatures (e.g., GPG signatures) or checksums (SHA-256) for all distributed binaries. Document the verification process for users. Consider using a reproducible build process. - Low · Missing Explicit Security Headers Configuration Visible in Codebase —
Server configuration (not fully visible in provided structure). Based on the file structure provided, there is no visible configuration for HTTP security headers (e.g., Content-Security-Policy, X-Frame-Options, Strict-Transport-Security, X-Content-Type-Options) in the accessible configuration files. For a self-hosted photo management application, missing security headers can expose users to clickjacking, MIME-type sniffing, and XSS attacks. Fix: Ensure the web server or application framework is configured to send appropriate security headers. Consider using helmet.js (for Node.js), or configure headers at the reverse proxy level (nginx/traefik). Implement a strict Content-Security-Policy. - Low · GitHub Workflow Files Could Be Vulnerable to Script Injection —
.github/workflows/. The repository contains numerous GitHub Actions workflow files (.github/workflows/). GitHub Actions workflows that use ${{ github.event.* }} or other user-controlled context variables in run steps without proper sanitization can be vulnerable to script injection attacks, where a malicious actor could craft a pull request or issue title to inject arbitrary commands. Fix: Audit all workflow files for use of user-controlled context variables in run steps. Use intermediate environment variables to safely handle user input (e.g., set ENV_VAR='${{ github.event.pull_request.title }}' and then use $ENV_VAR). Pin all third-party GitHub Actions to specific commit SHAs rather than tags. Use zizmor (already referenced in org-zizmor.yml) to audit workflows regularly. - Low · Cloudflare Module Configuration Potentially Exposed —
undefined. The deployment/modules/cloudflare directory suggests Fix: undefined
LLM-derived; treat as a starting point, not a security audit.
Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.