curl, and Python.
Two APIs, two jobs
The Ornn API manages the cluster and its capacity. The standard Kubernetes API runs your workloads on that capacity. Use any Kubernetes client library or
kubectl. Never send the Ornn API key to the Kubernetes API server, or the kubeconfig token to api.ornn.com.
The API reference documents every Ornn operation used here. Download the OpenAPI contract to generate a client.
What the API does not do
- No gRPC or GraphQL. The Ornn API is JSON over HTTPS.
- No purchasing. A cluster runs on hosts your organization already reserved. Reserve capacity before you automate launches.
- No automatic scaling of Ornn capacity. Ornn does not provide a Cluster Autoscaler or Karpenter integration, and pending Pods do not add workers. To change capacity, your automation calls the add and remove worker endpoints.
- One cluster at a time. Your organization can run one Kubernetes cluster, and cannot run a Kubernetes cluster and a Slurm cluster at the same time. A cluster counts until its teardown finishes; a failed launch does not count. Grow the cluster you have by adding workers.
- No credential renewal. The kubeconfig token is issued for seven days. Reading access again returns the same token. Contact Ornn support for new credentials.
- No external clusters. You cannot attach Ornn workers to an existing EKS, GKE, or AKS cluster.
- No single-cluster read. There is no
GETfor one cluster. Read the cluster list and select by ID.
Before you start
- Belong to an approved organization and create an API key in Account Settings → Developer.
- Hold an active reservation with any required payment complete. Its hosts must be online.
- Save data on the hosts you plan to use. Ornn prepares each host before it joins a cluster: running processes are stopped, the tenant home directory is wiped, and any VM on the host is removed.
- For Python, install
requestsand the Kubernetes Python client:pip install requests kubernetes. For shell, installcurlandjq. - If you choose a Private cluster, the machine that calls the Kubernetes API needs an active WireGuard connection to your organization’s network. See Private access with WireGuard.
ornn login and does not accept API keys. In scripts, pass --format json, --yes, and a stable --idempotency-key to CLI commands that change resources. See Human and automation modes.
Set your environment once:
detail.code, and retries only requests that are safe to retry:
429 is returned before your request reaches Ornn’s services, so any method is safe to retry after Retry-After. Do not blindly retry a write after a timeout or 5xx; read the current state first, as each step below describes.
1. Find a reservation and eligible hosts
List your active reservations, then list the hosts on the one you choose. A host can join as a worker when all of these are true:statusisbare-metal.controller_idisnull, so it is not in another cluster.health_readiness.readyistrue.
health_readiness.reasons containing heartbeat:stale means the host has not reported recently, and launch refuses it. heartbeat:unknown in advisory_reasons means Ornn has no heartbeat evidence either way; it does not block launch, but it does not prove the host is reachable. Node count means hosts, not GPUs.
ornn context --format json returns it, and so does GET /v1/auth/session with your API key.
2. Create the cluster
Sendkind, reservation_id, and the exact node_ids to use. Listing hosts explicitly is the safe choice: if you omit both node_ids and virtual_machine_ids, Ornn uses every bare-metal host on the reservation. Set network_mode to private, and optionally network_id, for a cluster reachable only over WireGuard.
202 Accepted with the new cluster’s id, state: provisioning, and its namespace. That response means the launch was accepted, not that anything is ready; node_count and gpu_count are usually 0 until workers attach.
Create is safe to repeat for the same reservation, but it is not a scaling operation:
- If the reservation’s cluster is
active, orprovisioningwith workers attached, the request returns that cluster unchanged with202, even ifnode_idsdiffers. - If the last launch on the reservation
failed, the request relaunches it under the sameid. - Ornn does not read an
Idempotency-Keyheader on this route. After a timeout or5xx, list your Kubernetes clusters before sending the request again, as the Python example does. Your organization runs at most one at a time, so a live cluster in that list is the one your earlier request created.
3. Wait for the cluster to become active
Poll the cluster list every 10 seconds and select your cluster bycontroller.id. Read three things:
controller.state:pendingorprovisioningwhile launching,activewhen every launch worker has joined and access has been issued,failedwhen the launch could not finish.controller.progress.stages: launch stagescreate_plane,prepare_hosts,join_workers,apply_plane, andissue_access, eachpending,running,completed, orfailed. A failed stage’sdetailcarries the reason.nodes[].statefor each worker:joining, thenready;join_failedmeans that host could not join.
failed after about 30 minutes, so a client deadline of an hour leaves room for the rest. If your deadline passes while the cluster is still provisioning, contact Ornn support with the cluster ID; do not launch again.
If a worker stays joining, check its host’s health_readiness in the host list from step 1. heartbeat:stale means the host went offline.
active means Ornn finished its part. It does not prove each Kubernetes Node is Ready or advertises GPUs; step 5 checks that through the Kubernetes API.
4. Fetch the kubeconfig
Request the cluster’s access. The response containskubeconfig, the API server endpoint, and namespace, the namespace Ornn configured for your workloads. A 404 with kubernetes_credentials_not_ready means access has not been issued yet; keep polling step 3. Any other 404 means the cluster ID is wrong or not visible to your organization.
The kubeconfig’s token has cluster-admin rights and is valid for seven days. Store it like a password, with owner-only file permissions.
0600 permissions and refuses to overwrite an existing file unless you add --overwrite.
5. Run a GPU smoke Job through the Kubernetes API
From here on you talk to the cluster’s own API server with the kubeconfig. Run GPU work in the namespace from step 4: in that namespace, Ornn’s scheduling rules place Pods on your reserved workers, and a ResourceQuota namedornn-gpu-quota caps the GPUs its Pods can request. Namespaces you create yourself do not receive those scheduling rules.
This NVIDIA example checks the Nodes, requests one GPU, runs nvidia-smi, and waits up to five minutes. The Node list also shows the managed control-plane Nodes, which are not GPU capacity. For AMD capacity, use an image with ROCm, request amd.com/gpu, and run amd-smi list.
6. Scale workers up or down
Scaling means adding or removing whole hosts through the Ornn API. Kubernetes autoscalers change Pod replicas, not Ornn hosts.Add a worker
Choose an eligible host as in step 1, preferably from the reservation the cluster launched on, and add it. A new host normally returnsstate: cleaning while Ornn prepares it. It then appears in the cluster’s nodes as joining, then ready. Repeating the request is safe: it returns cleaning until preparation finishes, then the member’s current state.
Adding a worker does not raise
ornn-gpu-quota, which Ornn sizes from the cluster’s workers when
it first becomes active. A host from a different reservation is labeled with its own reservation,
while Pods in your namespace are pinned to the reservation the cluster launched on, so they do not
schedule there. After the worker is ready, confirm in Kubernetes that its Node is Ready with
GPUs and that the quota covers the GPUs you plan to request. Contact Ornn support if it does not.404 controller_not_found when the cluster is failed or being torn down, 404 node_not_found when the host is not yours, and 409 cluster_nodes_not_available when the host is not in bare-metal status. A 409 with an empty JSON object means the host already belongs to another cluster.
Remove a worker
Removing a worker stops the workloads on it and can lose its local data. Checkpoint first.
Removing a worker does not cancel its reservation or stop its billing.
200 with state: draining, which means cleanup was sent. The worker leaves the cluster’s nodes once its host confirms cleanup. cleanup_failed means the host reported that cleanup did not finish; contact Ornn before reusing it. Repeating the request while the worker drains sends cleanup again; after it has left, the request returns 404 node_not_found.
Drive scaling from your own controller
Because Ornn does not watch your Pods, a scaling loop lives in your automation. A typical loop reads pending GPU Pods and Node capacity from the Kubernetes API, decides how many hosts it needs, and calls the add or remove worker endpoint for one host at a time, waiting forready or removal before the next change. Scaling is bounded by the hosts your organization has reserved; it never buys capacity.
7. Tear down the cluster and confirm
Teardown deletes the cluster’s workloads and revokes its kubeconfig. Export data first.
Reservation billing continues, and separately provisioned shared filesystems are not deleted.
202 with state: deleting. Ornn deletes the cluster’s namespace and workloads, then waits up to five minutes for each worker to confirm cleanup before removing the cluster. Hosts that never confirm are released anyway and cleaned when they reconnect. Repeating the request during teardown returns {"state": "deleting"} and does not restart cleanup.
Teardown is complete when both of these are true:
- The cluster no longer appears in the cluster list.
GET /v1/orchestrator/controllers/{cluster_id}/accessreturns404withcluster_not_found.
404 with no body. If the state becomes failed instead, send the teardown again, and contact Ornn support if it fails twice. Wait for teardown to finish before you create another cluster.
Retries and idempotency
The
/v1/orchestrator routes share a limit of 240 requests per minute for each user. Every API key you create counts against your limit, and so does your console session. Polling every 10 seconds uses a small fraction of it. A 429 includes Retry-After.
Cluster operations return errors as {"detail": {"code": "...", "message": "..."}}; show detail.message to people and branch on detail.code. Authentication and organization-approval errors use {"detail": "...", "code": "..."}. Malformed JSON, unknown request fields, or a missing Content-Type return 400, 422, or 415 with a plain-text body. See errors and safe retries.
Related
Kubernetes access
Console launch, networking, storage, and running applications on your cluster.
Cluster API reference
Request and response schemas for every cluster and worker operation.