Tell what limits Immich by reproducing one fixed upload, browse, search, or background-job workload and correlating its completion rate with CPU saturation, memory pressure, storage latency, and network throughput over the same interval.
A high percentage alone is not a bottleneck: full CPU may be healthy during machine learning, high RAM may be filesystem cache, and a slow upload may be the phone or Wi-Fi. Measure from client through container and host, capture queue progress, then change one suspected constraint and repeat. The resource whose relief improves the same workload is the controlling limit.
Build One Repeatable Test and Timeline
Choose the symptom you need to improve: ingesting a fixed media batch, opening uncached originals, generating thumbnails, running Smart Search, or transcoding one video. Record start, first usable result, completion, failures, and job-queue movement. Mixing tasks produces resource graphs without a decision.
One user investigation describes an unusually slow Immich deployment while seeking CPU, memory, disk, and network evidence. Its cross-resource diagnostic framing is useful, but your fixed test must determine the cause on your server.
Repeat once after caches are warm. If the second run is much faster, label cache state rather than calling the hardware inconsistent. If both runs stall at the same stage, align that timestamp with host and container metrics and the responsible job.
Identify CPU and Memory Signatures
CPU limitation shows sustained runnable work on the relevant cores while the job advances proportionally; lower concurrency may improve interaction but lengthen drain time. If one thread is full while total CPU looks modest, inspect the per-process and per-core view before assuming spare capacity.
Memory limitation requires pressure evidence: growing swap, reclaim, major faults, OOM kills, or container restarts. High used memory with stable cache, no swap, and normal latency does not fail the test. Rerun with one lower-concurrency worker or a smaller model and compare completion.
A reported Immich v2.5.5 thumbnail case consumed extreme memory on one deployment. The version-bounded memory report supports checking the failing job and release; it does not establish a normal RAM requirement.
Separate Storage Latency From Network Throughput
For storage, observe device latency, queue depth, throughput, filesystem space and inode availability while the exact job runs. Low megabytes per second can still be storage-bound when many small database and thumbnail operations wait on a high-latency disk.
For network, measure at both client and server, then compare local and remote paths. A saturated link, retransmits, Wi-Fi retries, or VPN ceiling that coincides with the transfer indicates a network limit. If upload traffic ends but processing remains slow, follow the server-side queue instead.
The ZimaSpace overview of services on older hardware provides system-planning context; this diagnosis should still rely on observed latency and work rate rather than age labels.
Relieve One Constraint and Verify the Same Workload
Change one safe variable: reduce one job's concurrency, add a temporary memory limit test, move a copy of active data to faster storage, or test over wired local networking. Keep dataset, versions, and cache state comparable. Improvement in both completion and the predicted metric passes the causal test.
Do not buy hardware from an idle average or a single peak. A resource is worth upgrading when it repeatedly controls the important workload after software errors, free-space problems, and competing scheduled work are excluded.
Roll back changes that shift failure to another layer or lengthen queues beyond the service target. Escalate with workload definition, timestamps, per-container metrics, disk latency, network tests, queue progress, and logs if the system stalls while no resource shows pressure; that pattern may be a lock, dependency, or application fault.
Support & Tips
More to Read

How to Optimize Immich Database Connections for Concurrent Containers
Do not raise max_connections first. Measure Immich sessions, total every container's demand, preserve admin headroom, and tune only the proven bottleneck.

How to Prevent Duplicate Jobs or Imports in Immich
Separate repeated jobs from duplicate assets. Use one canonical ingestion path, control retries and path changes, then test re-entry on a small cohort.

How to Repair Immich After Its Database Volume Fills Up
Never delete PostgreSQL WAL to free space. Stop Immich writes, preserve database state, add safe capacity, recover PostgreSQL, then prevent recurrence.

