End-user CLI for deploying apps to urun
Project description
urun CLI
Deploy Python apps to urun from your terminal.
Install
uv tool install urun-cli
# or
pip install urun-cli
The package installs the urun command:
urun --version
For one-off uvx usage:
uvx --from urun-cli urun --version
# or the package-matching command alias
uvx urun-cli --version
Quick start
Today, an operator manually vends an org-scoped deploy API key. Save it locally
with urun login:
urun login --api-key urun_<32hex>
urun login verifies the key with the urun API and stores credentials for later
commands. The future browser-based login flow is not available in this CLI
release.
For CI or one-off commands, you can still use the environment variable:
export URUN_API_KEY=urun_<32hex>
Create 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)}
Run it:
urun run app.py
In this release, urun run uses the same deploy pipeline as urun deploy.
deploy remains available as the lower-level command while the full
deploy/run/monitor workflow is being built.
Inspect apps
List every app deployed in your org and its current status:
urun list apps
Sample output:
APP FUNCTION COMPUTE STATUS RELEASE DETAIL
causal-forcing-stream generate_video h100:1 ready fa61f31b0961 0/1 GPU units in use
queued-app warmup a10:1 provisioning 000000000000 building
The STATUS column is one of provisioning, ready, pending, paused, or
failed. It is derived from three backend signals reporting on sequential
lifecycle phases:
| Build (S3 status) | Promotion (app_deployments) |
Capacity (function_ready) |
STATUS |
|---|---|---|---|
queued | building |
(no row yet) | - | provisioning |
failed |
(no row yet) | - | failed |
ready |
active |
false |
pending |
ready |
active |
true |
ready |
ready |
paused |
(irrelevant) | paused |
ready |
failed |
(irrelevant) | failed |
The DETAIL column carries the disambiguating signal (raw build state, error
message, ready_reason, or in-use GPU counts). Pass --json for the raw
payload.
This command is experimental and requires the server-side GET /apps
endpoint, which is in development.
Inspect sessions
List live and historical sessions in your org. Newest sessions appear at the
bottom of the table so the command works well with tail:
urun list sessions
urun list sessions | tail -20
urun list sessions --state failed
urun list sessions --limit 500
Sample output:
ID APP FUNCTION SHAPE STARTED DURATION STATE DETAIL
2cc8a91f4b3d helios world_gen h100:4 2026-06-02 10:55 UTC 44s failed no_capacity
3f0017daee01 helios world_gen h100:1 2026-06-02 11:08 UTC 18m43s completed client_disconnect
4a1c886e2d0a causal-forcing-… generate_video h100:1 2026-06-02 14:21 UTC 3m12s live -
The STATE column maps the raw backend status to a user-friendly label:
| Backend status | STATE |
|---|---|
allocated |
starting |
connected |
live |
closed |
completed |
failed |
failed |
cancelled |
cancelled |
DURATION is computed from allocated_at to closed_at for terminal sessions,
or allocated_at to now for live ones. DETAIL carries close_reason when
present. Pass --json for the raw payload (full IDs, ISO timestamps, all
fields).
Pass --limit to control how many rows are fetched (default 100).
This command is experimental and requires the server-side GET /sessions
endpoint, which is in development.
Inspect active compute
List the compute slices your org currently has provisioned:
urun list compute
Sample output:
APP FUNCTION SHAPE INSTANCES GPU UNITS SESSIONS AGE
causal-forcing-stream generate_video h100:1 1/2 1/2 1 12s
helios world_gen h100:4 0/1 0/4 0 3m
Each row is one actively provisioned (app, function, compute_shape)
slice. INSTANCES and GPU UNITS show <allocated>/<provisioned> — a
row with 0/1 is an idle warm runtime with no active sessions on it.
SESSIONS is the live session count. AGE is how stale the capacity
snapshot is; very old ages may indicate the runtime is no longer
reporting.
Slices with no provisioned capacity are omitted, so this command answers
"what is running right now". For the full deployment catalogue
(including paused / failed / unprovisioned apps) use urun list apps;
for historical or in-flight sessions use urun list sessions.
Pass --limit to control how many rows are fetched (default 100).
This command is experimental and requires the server-side GET /compute
endpoint, which is in development.
Manage apps
Manage the lifecycle of a single deployed app. The app is addressed by its
slug (the name shown under APP in urun list apps); every operation is
org-scoped via your API key.
Show detailed status for one app (the single-app complement to list apps):
urun app status lingbot
App: lingbot
Name: LingBot
Environment: prod
App status: active
Deployment: active
Desired replicas: 2
Function: handle_lingbot_runtime
Compute: b200:4
GPU: 4 x b200
Release: 1c6d6287abcd
Live sessions: 1
Scale an app's runtime replica count (the backend's scaling knob; the control plane turns it into the runtime StatefulSet replica count):
urun app scale lingbot --replicas 3
urun app scale lingbot --replicas 0 # drain to zero without retiring
GPU count and compute shape are fixed at deploy time per release (set via
@app.function), so scale intentionally exposes only --replicas.
Retire an app so the control plane stops running it (drives the deployment
to paused and the app to disabled, so the materializer stops recreating
its runtime). This is the clean, reversible, API-driven alternative to a
manual database edit:
urun app delete lingbot-handle # prompts for confirmation
urun app delete lingbot-handle --yes # or urun app rm lingbot-handle --yes
Reverse a retire and bring the app back online:
urun app activate lingbot-handle
All app subcommands accept --environment (default prod) and --json.
These commands are experimental and require the server-side app
lifecycle endpoint, which is in development.
What gets deployed
urun deploy creates a source manifest from your Python entrypoint:
| Entrypoint | Included source |
|---|---|
urun deploy app.py |
app.py and local Python files it imports |
Dependencies are declared in your urun app code. Project-level files such as
pyproject.toml and requirements.txt are not uploaded as dependency
declarations by the CLI.
Generated/cache content such as .git, dotfiles, __pycache__, and .pyc
files is excluded. Add .urunignore to exclude additional paths.
Non-Python assets such as templates, static files, and data files are not auto-included yet.
Common options
Shared by run and deploy:
| Option | Description |
|---|---|
--name |
Override the derived app name. |
--api-url |
Override the API URL; defaults to URUN_API_URL, saved login credentials, or https://api.urun.sh/v1. |
--api-key |
Deploy API key; defaults to URUN_API_KEY or saved login credentials. |
--no-wait |
Finalize but do not poll for readiness. |
--poll-interval, --timeout |
Control readiness polling. |
Troubleshooting
| Error | Fix |
|---|---|
missing API key |
Run urun login, set URUN_API_KEY, or pass --api-key. |
invalid API key format |
Use urun_<32 lowercase hex chars>. |
entrypoint not found |
Run from the project root or pass the entrypoint path. |
path is outside the project root |
Move the file under the project before deploying. |
| Expected files are missing | Import local Python files from app.py; non-Python assets are not auto-included yet. |
Development
Contributing and test instructions are in CONTRIBUTING.md.
License
MIT.
Development environment
This repo has a Nix/direnv/devcontainer baseline:
direnv allow
just sync
just check
Use VS Code Dev Containers to open the repository with the same toolchain in a container. Copy devcontainer.env.example to .devcontainer.env if you need to pass local git identity or other non-secret development settings into the container.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file urun_cli-0.5.0.tar.gz.
File metadata
- Download URL: urun_cli-0.5.0.tar.gz
- Upload date:
- Size: 34.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
861a20be6f2ac987f1def4d3e3702b62d8e656eb4e89f7ede9e1efbe0aa94efe
|
|
| MD5 |
f66d8f04bdd1d50a3e5fad40ee5222b8
|
|
| BLAKE2b-256 |
eaa6d7fb1891e8da042fd69e2ede8f2b0b67d1e056ef95daab1250169badf402
|
Provenance
The following attestation bundles were made for urun_cli-0.5.0.tar.gz:
Publisher:
release.yaml on urun-sh/urun-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
urun_cli-0.5.0.tar.gz -
Subject digest:
861a20be6f2ac987f1def4d3e3702b62d8e656eb4e89f7ede9e1efbe0aa94efe - Sigstore transparency entry: 1866415111
- Sigstore integration time:
-
Permalink:
urun-sh/urun-cli@cca2c97d8cf9d74cc3bf9c71c3de2a0f66df2ac6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/urun-sh
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@cca2c97d8cf9d74cc3bf9c71c3de2a0f66df2ac6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file urun_cli-0.5.0-py3-none-any.whl.
File metadata
- Download URL: urun_cli-0.5.0-py3-none-any.whl
- Upload date:
- Size: 35.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b7f53cd3e1fc4a34f00b41fd7b294fa75652ad2a45cfbb30f4e77777b0d135b
|
|
| MD5 |
7961bc84ed37ed618cfb55202f3362ff
|
|
| BLAKE2b-256 |
a3b48cceb1b1c93af3ab37087c85deda9cd68b5be543e9457848aebc00fc6f18
|
Provenance
The following attestation bundles were made for urun_cli-0.5.0-py3-none-any.whl:
Publisher:
release.yaml on urun-sh/urun-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
urun_cli-0.5.0-py3-none-any.whl -
Subject digest:
2b7f53cd3e1fc4a34f00b41fd7b294fa75652ad2a45cfbb30f4e77777b0d135b - Sigstore transparency entry: 1866415149
- Sigstore integration time:
-
Permalink:
urun-sh/urun-cli@cca2c97d8cf9d74cc3bf9c71c3de2a0f66df2ac6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/urun-sh
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@cca2c97d8cf9d74cc3bf9c71c3de2a0f66df2ac6 -
Trigger Event:
push
-
Statement type: