Host
host
¶
Defines the launcher-owned Modal app that hosts a full Mirumoji instance
mirumoji modal deploy lets users run the entire stack (the FastAPI server
and the built React frontend) privately on their Modal account
The shared deploy lifecycle functions lives in mirumoji.modal
Compute
-
The deployed
mirumoji-hostapp keeps exactly one container warm for its entire lifecycle, so background tasks are never cut off by a scaledown, the in-process job queue keeps running, and the single-writerSQLitedatabase only ever has one instance -
By default that container is
CPU-Onlyand runs themodalbackend, offloading every GPU task to a second app (mirumoji-offload, the same one the localmodalbackend uses, reusing the samemirumoji configvariables) that always scales to zero, so a GPU is paid for only while a job runs -
Setting
MIRUMOJI_HOST_ON_GPUinstead runs that one container on a GPU with thelocalwhisper backend in-process, so no offload app is used (seebuild_host_appfor the two modes)
Persistence
-
A named
modal.Volumeis mounted atDATA_MOUNT, a path separate from the server's data path. The server reads and writes user data on the container's local disk, and a background task (seelauncher.modal.app) mirrors it to the volume, keeping media and database I/O off the slow volume FUSE layer whileModal's volume commits still persist it durably -
mirumoji modal deploycreates that volume in the user'sModalworkspace when it doesn't exist, andmirumoji modal stop -vallows the user to delete it at any time
Image
-
When deployed as a full mirumoji instance to
Modal, a singleFastAPIapp serves both the frontend and the server (unlike when running locally with docker compose, where the frontend is served by Nginx instead) -
The image is composed at runtime with Modal's Python SDK from the published backend image (CPU, or GPU for a GPU host) with the published frontend build copied in, so
mirumoji modal deployalways uses the latest published releases (see_host_image)
HF_CACHE_DIR = '/root/.cache/huggingface'
module-attribute
¶
Where the faster-whisper model cache lives in the backend images
The GPU host grafts this directory from MODAL_GPU_IMAGE (which pre-caches the
model) onto BACKEND_GPU_IMAGE, so the first transcription loads it from disk
instead of downloading it
build_host_app(env, host_config, *, version, on_gpu, nonpreemptible)
¶
Builds the deployable Modal host app with the user's config injected
Host Modes
-
By default the web container is
CPU-Onlyand runs themodalbackend, offloading transcription and conversion to the on-demandmirumoji-offloadGPU worker, so a GPU is paid for only while a job runs -
When
MIRUMOJI_HOST_ON_GPUis1the container runs on the GPU named byMIRUMOJI_MODAL_GPUwith thelocalwhisper backend in-process, so there is a single app and no offload worker, at the cost of an always-warm GPU. The transcribe backend is injected here so the CLI and the GUI never have to decide it
Injected Environment
-
envcarries the user's relevant resolved configuration (Modal Tokens + LLM Keys + Web Password + Modal Config) as an inlineSecret, so the container behaves like a local server with the frontend served alongside it -
The secret is passed inline and bundled with the deploy so that the user never has to manage a persistent Modal secret
Resources
-
host_configsizes the web container itself, socpuandmemoryare reserved rather than left to Modal's fractional default, which would throttle the server enough to fail its health check and get the container recycled -
max_inputssets how many requests the one container serves at once
Function Registration
-
webstays a plain module-level function soinclude_sourcecan re-import it cleanly by name in the container -
The endpoint decorators (
asgi_appwrapped inconcurrent) and the deploy-time config (image, volume, secret, scaling) are applied here at registration, so nothing decorateswebat module scope (a module-level web endpoint is re-imported as a barePartialFunctionthe runtime rejects, unlike the offload worker'sClspath)
Single Container
-
min_containers=1keeps one warm container so a background transcription is never cut off and the in-process job queue runs -
max_containers=1pins the count to one, since the server holds in-process state and writes a single-writer SQLite file on local disk (mirrored to the volume), so it can't run as multiple replicas -
The pinned count also makes a scaledown window unnecessary, so none is set
Non-Preemptible
-
MIRUMOJI_HOST_NONPREEMPTIBLE=1runs the CPU host on guaranteed capacity (a 3x price) so a spot reclaim never restarts it mid-job -
Modal forbids non-preemptible GPU functions, so this is rejected when combined with the GPU host
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
dict[str, str]
|
The environment injected into the container as an inline secret |
required |
host_config
|
dict[str, str]
|
The resolved host reservations (CPU cores, memory in MiB, and max concurrent requests), each already defaulted by the caller, that size the web container |
required |
version
|
str
|
The published image version to compose from |
required |
on_gpu
|
bool
|
Run the host on a GPU (a GPU host) with the in-process backend, instead of a CPU host that offloads to the worker |
required |
nonpreemptible
|
bool
|
Run the CPU host on non-preemptible capacity |
required |
Returns:
| Type | Description |
|---|---|
App
|
The configured |
Raises:
| Type | Description |
|---|---|
ModalError
|
If a host reservation is not a number, or if a non-preemptible GPU host is requested (an unsupported combination) |
ensure_host_deployed(env, host_config, *, version, on_gpu, nonpreemptible, force=False)
¶
Creates the modal.Volume if it doesn't exist and deploys the Modal
host app unless one with the same version and host mode is already live
Idempotent and identity-tracked through mirumoji.modal: it never
duplicates the app, rolls it forward when the resolved version changes, and
redeploys when the host mode (GPU / non-preemptible) or a reservation
changes at the same version (folded into the deploy tags, see below)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
dict[str, str]
|
The environment to inject into the container |
required |
host_config
|
dict[str, str]
|
The resolved host reservations sizing the
web container (see |
required |
version
|
str
|
The published image version to compose from and track |
required |
on_gpu
|
bool
|
Run the host on a GPU, instead of a CPU host that
offloads to the worker (see |
required |
nonpreemptible
|
bool
|
Run the CPU host on non-preemptible capacity |
required |
force
|
bool
|
Redeploy even when the same version is already live, to roll out a code or image change without a version bump |
False
|
Raises:
| Type | Description |
|---|---|
ModalError
|
If credentials are missing or the deploy fails |
web()
¶
Builds and returns the Modal host FastAPI application
Container-Side
-
This runs in the
Modalcontainer, so it imports the app factory lazily and points it at the frontend copied into the image -
The endpoint decorators (
asgi_app,concurrent) are applied at registration inbuild_host_app, not here, sowebstays plainly re-importable
Returns:
| Type | Description |
|---|---|
FastAPI
|
The host |