docs
Python SDKCore Concepts

Compute

Configure GPUs, CPUs, session length, and warm capacity for urun functions

Compute defines the hardware your function runs on and how long its sessions live — GPUs, CPUs, memory, warmth, and concurrency.

When you write @app.function(gpus="h100:8"), you are declaring a compute requirement. urun provisions the hardware, distributes your code across it via torchrun, and tears it down when the function completes. You declare what you need; urun handles the rest.

This page covers the specification formats, GPU types, session length, and warm capacity. For how functions are defined and registered, see App. For the full session lifecycle, see Sessions.

GPU Specification

The gpus parameter accepts a string in "type:count" format, a type name alone, or an integer. The string form is the canonical API.

from urun import App

app = App("realtime-app")

@app.function(gpus="h100:8")    # 8x H100 -- a distributed realtime pipeline
def world(ctx):
    ...

@app.function(gpus="h100")      # 1x H100 (count defaults to 1)
def restyle(ctx):
    ...

@app.function(gpus="a100:4")    # 4x A100
def avatar(ctx):
    ...

@app.function(gpus="l40s:2")    # 2x L40S
def voice(ctx):
    ...

@app.function(gpus=8)           # 8x default GPU type (H100)
def studio(ctx):
    ...

GPU Types

GPUSpec stringBest for
B200"b200:N"Realtime generative inference
H200"h200:N"Large models -- 141 GB HBM3e for models that exceed H100 memory
RTX PRO 6000"rtx6000:N"Inference -- 96 GB GDDR7 fits ~80GB-class models on a single card
H100"h100:N"Multi-GPU realtime pipelines -- high bandwidth for distributed inference
A100"a100:N"General purpose inference, widely available
L40S"l40s:N"Inference -- cost-effective serving for mid-size models
A10G"a10g:N"Inference -- 24 GB for small-model serving
L4"l4:N"Inference -- lowest-cost serving; the default for many catalog models

When you pass an integer like gpus=8 without specifying a type, uRun defaults to H100. See GPU Specifications for hardware details.

CPU Specification

For session work that does not need GPUs — agent orchestration, media muxing, API-backed apps that talk to external model providers — use the cpus parameter:

@app.function(cpus="128")       # 128 CPUs, single node
def orchestrate(ctx):
    ...

@app.function(cpus="8:128")     # 8 CPUs per node, 128 total (16 nodes)
def fanout(ctx):
    ...

@app.function(cpus=64)          # 64 CPUs, single node
def mux(ctx):
    ...

The "per_node:total" format lets you control multi-node CPU layout. For example, "8:128" provisions 16 nodes with 8 CPUs each.

GPU shapes follow the same node-allocation rule, just with a fixed 8 GPUs per node: the node count is ceil(gpus / 8). So gpus="h100:8" is 1 node, gpus="h100:16" is 2 nodes (8 GPUs each), and a sub-node shape like gpus="b200:4" is a 4-GPU group inside one node.

GPUs and CPUs are mutually exclusive

A function uses either GPU mode or CPU mode. When gpus is specified (and greater than 0), the function runs on GPU nodes. When only cpus is specified, it runs on CPU nodes. Distribution is handled via torchrun in both modes.

Session length: lease, max_session_s, session_idle_timeout

Sessions are honest about how long they live. Three distinct kwargs, three distinct jobs:

@app.function(
    gpus="h100:1",
    lease=3600,               # renewable: auto-renewed while the client is connected
    max_session_s=14400,      # hard cap: renewal refused past 4h, reason max_session_age
    session_idle_timeout=600, # idle kick after 10 min of no client activity (0 = opt out)
)
def serve(ctx):
    ...
  • lease — the renewable session length in seconds. While the client stays connected, the platform keeps renewing it, so a healthy session runs as long as it is in use. When the lease lapses (client gone, no renewal), the session ends and capacity is freed.
  • max_session_s — the hard, non-renewable wall-clock cap. The control plane refuses lease renewal once the create-time deadline passes, so the session ends honestly with terminal reason max_session_age — no silent half-dead sessions. None (default) = uncapped.
  • session_idle_timeout — attached-but-inert idle timeout. The platform default is 300s; the user is warned ~90s before ("Are you still there?" — any activity dismisses it), then the session ends with reason idle_timeout. Pass 0 to opt out. Headless clients that never report activity are exempt.

There is no timeout kwarg — it is retired and raises a TypeError. Session length is lease; the hard ceiling is max_session_s.

Warm capacity and scale-to-zero

  • warm — the idle replica floor: how many replicas stay resident when the app has zero live sessions. warm=0 scales to zero when idle; warm=N keeps N warm; omitting it keeps legacy always-on behavior at the serving replica count. Declaring warm on a GPU function (multi-rank included) also defaults snapshot=True: the app snapshots after its first successful boot + self-test, and every later wake restores from the snapshot instead of cold-booting.
  • max_concurrency — the maximum concurrent sessions for the function. The platform provisions capacity proportional to live+queued sessions, clamped by this limit (platform default 2). Operators can override it live with urun app concurrency.
  • sessions_per_pod — how many sessions one pod serves concurrently (platform default 1). Raise it for lightweight per-session work on a shared model.
  • replicas — the number of independent serving replicas.
@app.function(gpus="b200:1", warm=0, max_concurrency=4)   # scale-to-zero, snapshot wakes, up to 4 sessions
def restyle(ctx):
    ...

Environment variables are not config

env= passes environment variables to the remote process, but env vars are not an app-config mechanism. App behavior is configured by decorator kwargs and hardcoded constants — never by os.environ.get("APP_*") reads. The env vars that remain are external inputs:

  • Secrets — declared via credentials=Credentials(env=["HF_TOKEN"]), injected securely at runtime.
  • Runtime-injected valuesRANK, WORLD_SIZE, LOCAL_RANK, and friends, set by the launcher (read them through ctx.rank / ctx.world_size instead).
  • Third-party library tuning an external library reads itself (e.g. env={"NCCL_DEBUG": "INFO"} for debugging NCCL).

Environment variables are not secrets either

For API keys and tokens, use credentials=Credentials(env=["HF_TOKEN"]), which injects secrets securely at runtime — never plain env=.

Walk-Through

Two serving shapes for the same app — a big distributed pipeline and a lean scale-to-zero one:

from urun import App
from urun.core import Dependencies

app = App("compute-walkthrough")

DEPS = Dependencies(python=["torch>=2.5.0", "transformers", "diffusers"])

# Interactive world: 8x H100 running one distributed realtime pipeline.
# The lease renews while a client is attached, so a session lives as long
# as someone is playing.
@app.function(gpus="h100:8", deps=DEPS, lease=7200)
async def world(ctx):
    """Multi-rank realtime video generation across 8 H100s."""
    import torch
    pipe = load_pipeline(device=ctx.device)  # each rank holds its shard
    video = ctx.stream("video", codec="h264", fps=20)
    await (ctx.source(pipe.frames, device="cuda") | ctx.pace(fps=20) | video).run()


# Restyle: 1x L40S, scale-to-zero with snapshot wakes, 30-min hard cap per session
@app.function(gpus="l40s:1", deps=DEPS, warm=0, max_session_s=1800)
async def restyle(ctx):
    """Single L40S restyling a live camera feed."""
    model = load_model(device="cuda")
    video = ctx.stream("video", codec="h264", fps=20)
    await (ctx.source(model.restyle, device="cuda") | ctx.pace(fps=20) | video).run()

The key difference: the world app holds 8x H100 with a long renewable lease for as long as clients stay attached; the restyle app uses a single L40S that scales to zero when idle and wakes from a snapshot, with each session hard-capped at 30 minutes.

Next: Store

Store is urun's content-addressed object store for model weights, resident loaded models, and real-time streams. Learn about URI-based downloads, weights residency, caching, and streaming.

Store

Related:

  • App — Defining functions and the full kwarg set
  • Sessions — The session lifecycle in depth
  • Getting Started — Build and run your first streaming app

On this page