Performance & Hardware
Engram runs comfortably on modest hardware, but a few choices — GPU vs CPU for transcription, where you put the staging directory, and how many matches run in parallel — make a large difference to how fast a stack of discs gets through the pipeline. This page explains the knobs that already exist and how to plan hardware around them.
The expensive stages are ripping (disk + optical drive throughput, handled by MakeMKV) and episode matching (audio transcription via faster-whisper). Everything else — identification, organization, the dashboard — is cheap by comparison.
Hardware at a glance
These are practical recommendations, not enforced minimums. Engram will start and run on less.
| Component | Works on | Recommended | Why it matters |
|---|---|---|---|
| CPU | Any modern 64-bit CPU | 4+ physical cores | More cores let you raise max_concurrent_matches so several titles transcribe at once. |
| RAM | ~4 GB | 8 GB+ | The Whisper model plus ripping buffers; more headroom helps when matching several titles in parallel. |
| GPU | None — CPU is fully supported | NVIDIA GPU with CUDA | Optional. Speeds up matching only (not ripping). Auto-detected when present. |
| Storage (staging) | HDD / NAS | SSD on the same volume as your library | Transcription re-reads each file, and same-volume organization is an instant rename instead of a full copy. |
GPU acceleration (faster-whisper ASR)
Episode matching transcribes each ripped title with faster-whisper, which runs on NVIDIA CUDA via CTranslate2. GPU transcription is several times faster than CPU for the same model — the single biggest lever on matching throughput.
GPU acceleration is opt-in and available on Windows and Linux with an NVIDIA GPU. macOS and
AMD GPUs have no GPU path in the engine and always run on CPU (int8).
Enable it
In Settings → Matching → GPU Acceleration:
- When an NVIDIA GPU is detected, Engram offers a one-time Download & enable (after you accept
the NVIDIA CUDA EULA). The required cuDNN + cuBLAS
libraries (~1.2 GB) are not bundled into the build — they're fetched on demand into
~/.engram/cuda/, where they persist across app updates (like your database and subtitle cache). - The download runs in the background; you can keep using Engram while it completes.
- Restart the backend to activate. Matching then runs on the GPU using the best compute type
your card supports —
float16, orint8_float16/float32on older GPUs (e.g. Pascal) that can't dofloat16. CPU continues to useint8.
This works in the prebuilt standalone build — the CUDA libraries are downloaded at runtime, not
compiled in. (Developers running from source can instead install them via pip with
uv sync --extra gpu; see below.)
Docker
In a container, GPU access additionally requires the host NVIDIA driver, the
NVIDIA Container Toolkit, and running with
--gpus all. With those in place the same in-app download applies. See the
Docker guide.
Verify which backend is active
Engram exposes the resolved ASR runtime at GET /api/asr-status (also shown as the ASR badge on
the dashboard). The device field is the effective device — what transcription actually runs on
— so the badge can't claim CUDA while silently falling back to CPU:
{
"device": "cuda",
"compute_type": "float16",
"model": "small",
"workers": 4,
"cpu_threads": null,
"max_concurrent_matches": 4,
"gpu_detected": true,
"gpu_enabled": true,
"gpu_runtime_installed": true,
"gpu_state": "active"
}
gpu_state tells you exactly where you stand:
active— running on the GPU.available_not_enabled/available_not_installed— an NVIDIA GPU is present but acceleration is off, or the runtime hasn't been downloaded yet. The dashboard badge shows CPU · GPU available →; click it to open Settings.downloading/installing— the CUDA runtime download is in progress.unsupported_os— macOS or another platform with no NVIDIA path.unavailable— a supported OS with no NVIDIA GPU.
Running from source (developers)
When running from source you can install the CUDA libraries via pip instead of the in-app download:
Engram falls back to these pip-installed libraries automatically, so the GPU Acceleration setting reports the runtime already installed — just enable it and restart.
Whisper model
The matcher currently uses the small model (≈465 MB, English-capable, good accuracy on
CPU). It is hardcoded — there is no user-facing model selector yet. For reference, faster-whisper
models trade size for accuracy and speed:
| Model | Disk size | Quality | Speed | GPU |
|---|---|---|---|---|
tiny |
75 MB | basic | fastest | not required |
base |
145 MB | good | fast | not required |
small (default) |
465 MB | better | medium | not required |
medium |
1.5 GB | best | slow | recommended |
large-v3 |
3 GB | best | slowest | 10 GB+ VRAM |
The model downloads once on first use and is cached locally; larger models also use proportionally more memory (VRAM on GPU, RAM on CPU).
Storage: SSD vs HDD vs NAS
Ripped and imported MKVs land in the staging directory (staging_path) first, then get moved
into your library when a job completes.
Keep staging and your library on the same volume
Organization uses an atomic shutil.move(). On the same filesystem that's an instant
rename — no data is copied. Across different volumes (e.g. staging on your system SSD,
library on a NAS) it becomes a full copy-then-delete of every file, which is slow and uses
temporary double space. Put staging_path, library_movies_path, and library_tv_path on
the same drive when you can.
Beyond the move, transcription reads each title's audio repeatedly while sampling it. That makes staging I/O latency matter:
- SSD — recommended for staging. Fast random reads keep matching fed.
- HDD — works fine; expect somewhat slower matching and (cross-volume) organization.
- NAS / network share — usable for the final library, but avoid it for staging: network latency and seek times slow both transcription and the copy fallback. If you must stage on a NAS, expect noticeably longer matching.
Sizing the staging directory
Staging needs roughly (number of discs ripping at once) × (disc size) of free space, plus
headroom for the copy fallback if your library is on another volume. A dual-layer Blu-ray rip can
be 30–50 GB; a DVD is a few GB. Staging is reclaimed automatically per the
staging_cleanup_policy setting (default: delete on success).
Concurrency tuning
max_concurrent_matches (default 2) controls how many titles transcribe in parallel. It's the
main throughput knob once ripping is done.
How it works under the hood:
- One shared Whisper model serves all matches (using faster-whisper's
num_workers), so raising the value adds CPU/RAM pressure, not duplicated model weights/VRAM. - The value is clamped to your hardware by
resolve_asr_runtime(): on CPU it's capped at your physical core count (and CPU threads are divided across workers to avoid oversubscription); on GPU it's capped at 4 parallel streams. - It also sizes the matching admission semaphore, so at most that many titles show MATCHING at once; the rest wait in QUEUED. (You'll see this on the dashboard during a multi-title disc or bulk import.)
Guidance:
- CPU-only, multi-core: raising it to ~4–8 (within your core count) speeds up bulk work.
- GPU: keep it modest to avoid VRAM spikes; the GPU cap of 4 already bounds it.
Takes effect after a restart
num_workers is fixed when the Whisper model loads, so changes to max_concurrent_matches
apply on the next backend restart.
Bulk imports and large libraries
There's no special batch parallelism for big imports — many files and seasons are naturally
serialized by the matching semaphore, so steady-state matching throughput is roughly
max_concurrent_matches titles at a time. Practical advice for migrating a large library:
- Raise
max_concurrent_matchesto suit your cores/GPU before starting. - Make sure the precomputed subtitle cache is in place (it ships and installs on first run) so matching isn't waiting on subtitle downloads.
- Kick off large imports when you don't need the machine, and watch the dashboard ASR badge to
confirm
workers > 1.
See the Import Watch Folder guide for how to feed Engram a whole library of pre-ripped files.
Troubleshooting performance
| Symptom | Likely cause | What to check |
|---|---|---|
| Many titles MATCHING but only one progresses; low CPU | Only one ASR worker is active | GET /api/asr-status — if workers is 1, raise max_concurrent_matches and restart |
| Matching slower than expected on a capable PC | Running on CPU unexpectedly | device in /api/asr-status; verify your CUDA install / use a --extra gpu source build |
| Organization step is slow / uses lots of temp space | Staging and library are on different volumes | Move them onto the same filesystem so the move is an atomic rename |
| Identification stalls or is slow | Network latency to TMDB / subtitle / TheDiscDB services | This affects identification only, not ripping; check connectivity |
| Staging fills up the disk | Concurrent rips × disc size exceeds free space | Free space, or stagger rips; confirm staging_cleanup_policy |