Deploy apps
Package and ship a Python app to urun GPUs with urun deploy
urun deploy takes a Python entrypoint, packages the app, uploads it, and builds it on
the control plane until the function is ready to serve sessions.
A minimal app
A uRun app is a Python file that creates an App and decorates one or more functions.
The GPU shape lives on the @app.function decorator — it is deploy-time config, not
something a session can override.
# app.py
import urun
from urun import App
app = App("hello-h100")
@app.function(gpus="h100:1")
def hello(ctx: urun.Context):
print(f"running on {ctx.device}")
return {"device": str(ctx.device)}See the Python SDK for the full decorator surface (deps,
credentials, multi-GPU gpus shapes like "b200:4", and session length via lease /
max_session_s).
Deploy it
urun deploy app.pyDeploying hello-h100 (fa61f31b0961)
Files: 1 app, deps: pip
Uploading 3 missing blob(s)
Deployment queued: 7c9e1a...
Waiting for build to complete (--no-wait to skip)
readyWhat urun deploy does
discover files → resolve deps → build manifest → register manifest
→ upload missing blobs (content-addressed) → finalize → poll build- Discover & resolve — finds the app's source files and resolves its declared dependencies.
- Manifest — builds a content-addressed manifest and registers it. The server replies with which blobs it already has.
- Upload — uploads only the blobs the server is missing (content-addressed, so re-deploys of unchanged files upload nothing).
- Finalize — finalizes the manifest, queuing the build.
- Poll — polls the build to readiness, printing progress. On failure the CLI prints the real build stderr so you can debug the image.
What ships
File discovery is automatic — there are no include flags:
- The Python import closure of your entrypoint (every
.pyyour app imports from the project). - Sibling data files — every non-
.pyfile sitting in the same directory as a collected.pyfile ships too (aworkflow_api.json, a prompt template, a small config read at import time). Data rides with its code: directories containing no collected code (weights/,node_modules/, media assets) contribute nothing, dotfiles and__pycache__are excluded, and dependency metadata (requirements.txt,pyproject.toml, lockfiles) travels via the deps channel, never as data. store.repodelta artifacts —diff=/overlay=files referenced by string literal are collected; a missing one is a hard error.
A .urunignore file excludes paths from collection (ignored code that the app imports
fails loudly; ignored data is silently skipped). Ship large assets (weights, media)
through the store, not the deploy bundle.
Flags
| Flag | Default | Effect |
|---|---|---|
--name NAME | derived from the entrypoint file | Override the app name |
--environment ENV | resolved config or main | Deployment environment (alias --env) |
--no-wait | off | Finalize the deployment but don't poll for readiness |
--poll-interval SECS | 2.0 | How often to poll the build status |
--timeout SECS | 1800.0 | Give up polling after this many seconds |
--api-url URL | https://api.urun.sh/v1 | Control-plane base URL |
--api-key KEY | from env / saved login | Deploy API key |
Fire-and-forget
To queue a build and return immediately (for example in CI where a later step watches status), skip the poll:
urun deploy app.py --no-waitThen check readiness separately:
urun app status hello-h100Use urun deploy. urun run is an alias: it accepts the same flags and runs the
exact same deploy pipeline. Prefer urun deploy for clarity.
Next
- Manage the deployed app — status, scale, retire.
- Watch sessions once clients start connecting.