Choosing a runtime
Modal runs all your code in containers that start from an image, on CPUs or GPUs you choose. Its runtimes differ in what starts a container and what stops it: Modal itself, scaling with calls or requests, or your code, creating a sandbox and terminating it.
Modal manages less as you decide more
Section titled “Modal manages less as you decide more”The same program on every runtime
Section titled “The same program on every runtime”A Function, a Server and a program Sandbox are each a class that holds its code. The program inside is the same shape, an Effect that returns fetch and any methods you want to call:
Effect.gen(function* () { return { fetch: Effect.succeed(HttpServerResponse.text("ok")), embed: Effect.fn(function* (text: string) { return { tokens: text.split(/\s+/).length }; }), };});Alchemy bundles main into an image, and Modal builds it at deploy.
Functions: scale with calls
Section titled “Functions: scale with calls”A Modal.Function runs when it’s called. Modal starts containers as calls arrive, runs up to maxConcurrentInputs in each, and stops them when they’re idle. Callers use its methods by name, a schedule calls it on a cron, or web: true serves its fetch at a *.modal.run URL.
export default class Embed extends Modal.Function<Embed>()( "Embed", { main: import.meta.url, app: Inference, gpu: "L4" }, program,) {}Choose it for inference, batch jobs and anything you fan out. See Functions.
Servers: scale with concurrent requests
Section titled “Servers: scale with concurrent requests”A Modal.Server is an HTTP server behind one routing region. Each container serves many requests at once, and Modal adds a container when they pass targetConcurrency.
export default class Api extends Modal.Server<Api>()( "Api", { main: import.meta.url, app: Backend, targetConcurrency: 20 }, program,) {}Choose it for APIs, streaming responses and WebSockets. See Servers.
Program sandboxes: your program, one container per create
Section titled “Program sandboxes: your program, one container per create”A Modal.Sandbox with a main bundles your program into an image. Your code starts one with create, reaches the program’s port through a tunnel, and terminates it, or lets its timeout stop it.
const sandboxes = yield* Modal.CreateSandbox(MySandbox);const sandbox = yield* sandboxes.create({ timeout: "10 minutes" });const port = yield* sandbox.getTcpPort();Choose it when each user or session needs a copy of your own server. See Programs.
Sandboxes created at run time: one per task
Section titled “Sandboxes created at run time: one per task”Modal.CreateSandbox bound to an image gives a Worker or Lambda a client that creates sandboxes for code you don’t trust. Your code runs commands in each one, reads and writes its files, and terminates it when the task is done. idleTimeout stops one nobody is using.
const sandboxes = yield* Modal.CreateSandbox(Warm);const sandbox = yield* sandboxes.create({ idleTimeout: "10 minutes" });yield* sandbox.exec(["pnpm", "test"], { cwd: "/work" });Choose it for agent loops, code interpreters and test runs. See Sandboxes.
A persistent sandbox keeps its files between sessions instead, and resumes where the last one stopped.
Mix runtimes in one app
Section titled “Mix runtimes in one app”A Worker creates a sandbox per user and calls a GPU Function, and both read model weights from one Volume: