MCPcopy Index your code
hub / github.com/epheterson/immich-apple-silicon

github.com/epheterson/immich-apple-silicon @v1.5.25

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.5.25 ↗ · + Follow
437 symbols 1,163 edges 11 files 171 documented · 39%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Immich Accelerator

Release License macOS Homebrew Immich

Beta. In daily use on a Mac Mini M4 (24GB) against an Immich 2.7.x library. Stable, but back up your Immich database before your first run.

Run Immich's compute natively on Apple Silicon. Thumbnails use the fast M-series CPU, video transcoding uses VideoToolbox hardware encoding, and ML runs on Metal GPU, Neural Engine, and CoreML.

Docker handles the lightweight parts (API server, Postgres, Redis). The accelerator runs Immich's own microservices worker natively on macOS, giving it access to hardware that Docker can't reach.

How it works

Docker (lightweight)                 Native macOS (compute)
+-----------------------+           +-------------------------------+
|  immich-server (API)  |           |  Immich Accelerator           |
|  postgres             |<--------->|  +- Microservices worker      |
|  redis                |  DB+Redis |  |  +- Sharp (thumbnails)     |
|                       |           |  |  +- ffmpeg (VideoToolbox)  |
|  WORKERS_INCLUDE=api  |           |  +- ML service                |
|  ML_URL=host:3003     |           |     +- CLIP (MLX/Metal)       |
+-----------------------+           |     +- Faces (Vision/ANE)     |
                                    |     +- OCR (Vision/ANE)       |
                                    +-------------------------------+

The microservices worker is extracted directly from your running Immich Docker image, so it tracks whatever version you run (verified with Immich 2.7.x and 3.0.x). Always the exact same version, no source builds. The only modification is installing the macOS-native Sharp binary for image processing. Video transcoding is intercepted by a lightweight ffmpeg wrapper that remaps software encoders to VideoToolbox hardware encoders.

What we modify (and how to undo it)

Nothing inside Docker is modified. We don't patch Immich, rebuild images, or replace containers. All changes are to your docker-compose.yml and can be reverted by removing a few lines.

What we change How Reversible? Risk
Add env vars to docker-compose IMMICH_WORKERS_INCLUDE, IMMICH_MACHINE_LEARNING_URL, IMMICH_MEDIA_LOCATION Remove the lines None
Expose Postgres/Redis ports 5432:5432, 6379:6379 in docker-compose Remove the port lines None
Native microservices worker Extracted from Docker image, runs via node Stop the accelerator None
Native ML service Separate Python service Stop the accelerator None
/build symlink (Immich 2.7+) /etc/synthetic.d/immich-accelerator (requires sudo once during setup) immich-accelerator uninstall removes it; reboot to deactivate Low

Why /build? Immich 2.7+ stores absolute plugin paths like /build/corePlugin/dist/plugin.wasm in its database. Both Docker and native workers need /build to resolve. macOS SIP prevents creating root-level directories, so we use Apple's synthetic link mechanism to map /build~/.immich-accelerator/build-data. Setup prompts for sudo once; a reboot may be required to activate.

To fully revert: Stop the accelerator, remove the env vars and port mappings from docker-compose, docker compose up -d. Immich is back to stock.

Requirements

  • macOS on Apple Silicon (M1/M2/M3/M4)
  • Homebrew

That's it. Setup installs everything else (Docker, Node.js, ffmpeg, ML dependencies).

Quick start

brew install epheterson/immich-accelerator/immich-accelerator
brew trust epheterson/immich-accelerator  # Homebrew 5.1.15+: lets brew upgrade see future releases
immich-accelerator setup

If no Docker is found, setup offers to install OrbStack. If no Immich is running, setup creates the entire Docker stack for you. Just answer two questions:

  1. Where are your photos? (e.g., ~/Pictures), mounted read-only for Immich to import
  2. Where should Immich store its data? (e.g., ~/.immich-accelerator/data) for thumbnails, transcoded video, and backups

Setup generates the docker-compose, starts Immich, extracts the native worker, and starts everything. Open http://localhost:2283 to create your admin account.

For existing Immich installs, setup detects the running containers and configures the accelerator to work alongside them.

For NAS + Mac setups, see Split deployment below.

Understanding IMMICH_MEDIA_LOCATION

This is the directory Immich uses as its media root. It contains these subdirectories: upload/, thumbs/, encoded-video/, library/, profile/, backups/. Both Docker and the native worker must see this directory at the same absolute path. Setup handles this automatically for same-machine installs.

Commands

Every command is prefixed with immich-accelerator (e.g. immich-accelerator setup).

Command What it does
setup Auto-detect local Docker, extract server, configure
setup --url URL Set up from a remote Immich instance
setup --manual Create a config template for manual editing
start Run worker + ML once in the foreground (testing only; use the service for auto-restart/update/log-rotation)
stop Stop native services
status Show what's running
logs [worker\|ml] Tail service logs
update Update to match a new Immich version
watch Monitor + auto-restart/update + log rotation (what the service runs)
dashboard Web UI at http://localhost:8420
ml-test Diagnose the ML service (health + CLIP + OCR round-trip)
uninstall Remove services, data, and launchd config

Dashboard

Real-time monitoring at http://your-mac:8420:

immich-accelerator dashboard

Shows service health, processing progress with live rates and ETAs, Apple Silicon hardware utilization, and system metrics. Mobile-friendly, check from your phone.

The dashboard and setup use the Immich API for job status and queue control. Create an API key from an admin account in Administration > API Keys with these permissions:

Permission Used by Why
job.read Dashboard Show queue activity (active/waiting counts)
job.create Dashboard Re-queue button
asset.read Setup Detect upload library path
library.read Setup Detect external library paths

All job.* and library.* endpoints require admin access. If the dashboard shows "API key invalid," make sure the key was created by an admin user.

Dashboard

Updates

The accelerator handles Immich updates automatically:

  • On every start: checks the Docker container version, re-extracts if it changed
  • In watch mode: checks every 5 minutes. If Watchtower or a manual docker compose pull updates Immich, the watchdog stops the worker, re-extracts the new server, and restarts. No manual intervention needed.
  • Manual: immich-accelerator update if you prefer to control the timing

To update the accelerator itself:

brew upgrade immich-accelerator

If you run it as a service (watch mode, the recommended setup), that's all you need: within ~30s the watcher notices the new version on disk, relaunches itself, and restarts the worker and ML service on the new code. (A detached worker survives a plain restart, so this version-aware reload is what guarantees the new code actually takes effect; brew services restart alone wouldn't reload the worker.)

If you run the worker manually instead of as a service, restart it yourself after upgrading:

immich-accelerator stop && immich-accelerator start

If brew upgrade says there's nothing to do but you know a newer release exists, see Troubleshooting.

Performance tuning

In the Immich admin UI (Administration → Jobs), tune the per-queue concurrency for your hardware. Recommended for M4 with 24GB:

Queue Concurrency Why
Thumbnail Generation 4 CPU-bound (Sharp/libvips with NEON SIMD)
Smart Search 2 GPU-serialized (MLX Metal, no benefit higher)
Face Detection 3 Neural Engine (Vision framework)
OCR 3 Neural Engine (Vision framework)
Metadata Extraction 4 I/O-bound (exiftool)
Video Conversion 1 Hardware-accelerated via VideoToolbox

Higher isn't always better. Oversubscribing the CPU causes thrashing and actually reduces throughput.

Split deployment (NAS + Mac, or any two hosts)

Run the Immich Docker stack (API, Postgres, Redis) on one host and the native accelerator (worker + ML) on another. The one rule: both machines must see the same files at the same absolute paths via a shared filesystem. Setup detects a path mismatch and refuses to save a broken config.

See docs/split-deployment.md for the full guide: topology, the two ways to align paths (match the Mac in Docker, or a synthetic link on the Mac), fresh-install geodata initialization, and changing IMMICH_MEDIA_LOCATION safely.

ML service

The ML service is a managed fork of immich-ml-metal by @sebastianfredette, included as a git submodule. It replaces Immich's Docker ML container with native macOS inference. Upstream changes are reviewed before merging.

Task Hardware Framework
CLIP embeddings GPU (Metal) MLX
Face detection Neural Engine Apple Vision
Face recognition CPU / CoreML InsightFace ONNX
OCR Neural Engine Apple Vision

Contributions to the ML service are made via upstream PRs.

Running as a service (recommended)

Run the accelerator as a background service, not with a bare immich-accelerator start. The service runs watch mode, and only watch mode gives you:

  • Auto-restart: launchd (KeepAlive) restarts the monitor if it dies; the monitor restarts the worker, ML, and dashboard if they crash.
  • Auto-update: picks up new Immich versions (re-extracts the worker) and notifies on accelerator updates.
  • Log rotation: caps worker.log/ml.log so they can't grow without bound.

A plain immich-accelerator start runs once in the foreground with none of the above. Use it only for quick testing.

Homebrew install (recommended):

brew services start epheterson/immich-accelerator/immich-accelerator

This uses the formula's own service definition, survives brew upgrade, and restarts at login. Check it with brew services list. Stop with brew services stop epheterson/immich-accelerator/immich-accelerator.

Don't also install the launchd plist below if you're using brew services. Running both double-starts the watcher.

Git / non-Homebrew install: immich-accelerator setup offers to install a launchd LaunchAgent (~/Library/LaunchAgents/com.immich.accelerator.plist). If you skipped that prompt, re-run setup and it will offer again.

Either way the service uses watch mode with KeepAlive. Confirm everything is healthy with immich-accelerator status and immich-accelerator ml-test.

Safety

  • Immich's Docker image is unmodified. No custom images, no patches.
  • The native worker runs Immich's own code. Extracted from the Docker image, not reimplemented.
  • UPSERT-safe database writes. The native worker uses Immich's own job pipeline with the same UPSERT logic.
  • Version-matched. The extracted server always matches the Docker image version exactly.

Known differences from Docker

The native worker runs Immich's unmodified code. The ffmpeg and image processing toolchain match Docker. The only differences are in the ML service, which uses Apple-native frameworks instead of ONNX Runtime.

Area Docker Native (Accelerator) Impact
ffmpeg Jellyfin-ffmpeg Jellyfin-ffmpeg (same binary, macOS arm64 build) Identical. Same tonemapx filter, same encoders, same behavior. Downloaded automatically during setup.
ffmpeg encoders Software H.264/HEVC VideoToolbox hardware H.264/HEVC via wrapper Hardware-encoded output has slightly different bitstream characteristics. Visually equivalent. A lightweight wrapper remaps Immich's software encoder requests to VideoToolbox hardware equivalents. Immich has no VideoToolbox option, so it logs Transcoding video ... without hardware acceleration even though the encode runs on the GPU via the wrapper. That log is expected and benign.
Sharp / libvips Prebuilt linux-arm64 Sharp Rebuilt against Homebrew system libvips Identical image output. System libvips handles corrupt HEIF files more gracefully (matches Docker's error handling).
ML: CLIP ONNX Runtime MLX on Metal GPU Same model, different runtime. Embeddings are numerically close but not identical (floating-point differences). Search results are equivalent.
ML: Face detection ONNX Runtime Apple Vision framework (Neural Engine) Different model entirely. Detection accuracy is comparable; bounding boxes may differ slightly.
ML: Face recognition ONNX Runtime ONNX Runtime with CoreML Same model,

Core symbols most depended-on inside this repo

Shape

Method 256
Function 127
Class 51
Route 3

Languages

Python97%
TypeScript3%

Modules by API surface

tests/test_accelerator.py176 symbols
immich_accelerator/__main__.py100 symbols
tests/test_fresh_install.py92 symbols
tests/test_dashboard.py37 symbols
immich_accelerator/dashboard.py14 symbols
immich_accelerator/hooks/pg_dump_shim.js6 symbols
immich_accelerator/hooks/heic_decode_shim.js5 symbols
immich_accelerator/hooks/pg_keepalive_shim.js4 symbols
tests/conftest.py3 symbols

For agents

$ claude mcp add immich-apple-silicon \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page