Create a pod
Runs your container image as a pod on gpuCount whole GPUs of one node, with SSH access, at the on-demand rate fixed at creation, and returns it as a VmSummary. Compute-fabric pod (BYO container image + SSH keys): a hardened GPU container on gpuCount whole native-driver GPUs of one node. Admission claims free device-request GPU units (see listPodCapacity for what is claimable), snapshots the on-demand rate immutably onto the pod, and drives the guest workflow. A valid request the fleet has no room for right now answers 503 with a capacity code; a GPU count no node could ever hold for the org answers 400 GPU_COUNT_NOT_ALLOCATABLE; and an org without the pod tier answers 409 FEATURE_DISABLED.
Required permission
Requires the compute.create permission. See Permissions for the roles and API key permissions that include it.
Command line
The orl CLI wraps this endpoint:
orl pods create --image <image> --ssh-key-ids <ssh-key-ids>apiKeyAuthorizationBearer <token>OpenRelay API key. Send it as Authorization: Bearer or_… (legacy vl_… keys still work).
Idempotency-Key?stringMakes a retried create safe. Choose one value per create you mean to make, 1 to 512 bytes (bytes, not characters), such as a UUID, and send it again on every retry of that create. The first request with a key runs, and a 2xx answer is kept for 24 hours: a repeat with the same key and the same body gets that answer back, with the same status and the header Idempotent-Replayed: true, and creates nothing. A batch create compares the body byte for byte; a VM or pod create compares the fields it decodes, so whitespace and key order do not count. A repeat while the first request is still running answers 409; repeat it a second later. The same key with a different body answers 422. An answer other than a 2xx is not kept, so a retry after a 4xx or a 5xx runs again. A 2xx too large to keep holds the key without it: every repeat answers 409 and creates nothing. Keys are scoped to the organization and the operation.
1 <= lengthapplication/json- body
Request to create a compute-fabric pod: a RunPod-style bring-your-own container image + SSH keys guest. A focused subset of CreateVmRequest (no qcow2 template / kernel modules / privileged mode).
name?stringgpuModelId?stringcomputeClass?stringpurchase option. on_demand (pay per second, no commitment) is the only class sold; reserved is a deprecated alias that is stored as on_demand. interruptible is retired and rejected with 400 COMPUTE_CLASS_RETIRED; it remains listed only until the deprecation window closes.
"on_demand""on_demand""reserved""interruptible"image*stringtenant container image ref (required)
gpuCount?integerhow many whole GPUs this pod gets. All of them are claimed on ONE node and passed to the container together; the pod is priced for gpuCount GPUs. Omitted = 1. A count no single node could ever hold for your org is refused with 400 GPU_COUNT_NOT_ALLOCATABLE; one that some node could hold but none has free right now is refused with 503 INSUFFICIENT_GPU_CAPACITY (maxGpuCount on /v1/pods/capacity is the largest a create can take now).
1 <= value <= 81sshKeyIds*array<string>Org SSH key ids injected into the guest. At least one is required and every id must name a live key of this org: the data-plane gateway authorizes guest SSH against exactly this list, so a pod created without one has no SSH access. Attaching or detaching a key later takes effect within about a minute, with no restart.
1 <= items <= 64envVars?httpPort?integerin-guest HTTP port exposed on the endpoint
80shmSizeGb?integersize of the pod's private /dev/shm in GiB (docker --shm-size). Optional; the maximum is half the pod's memory limit on its placement node (itself the pod's GPU-proportional share of node RAM), and a larger value is refused with 400 POD_ADMISSION_FAILED. Omitted lets the platform pick a safe default: that same maximum for a pod that owns its whole node, 2 GiB per GPU (capped at the maximum) for a shared multi-GPU pod, and docker's default for a shared single-GPU pod. Raise this when a multi-GPU NCCL / PyTorch DataLoader workload needs more shared memory than the default.
1 <= value <= 8192public?booleantrue = open endpoint (no data-plane auth); default false requires an org API key
falsevolumeGb?integersize in GB of the pod's persistent volume, mounted read-write at /workspace. Omitted = 50. The volume lives on the pod's node and is kept across stop, start, restart, container crashes, node reboots and interruptions; it is deleted only when the pod is deleted (or after 7 days interrupted with its node gone). Everything outside /workspace is the container's own filesystem and is reset whenever the container is recreated. Fixed at create; there is no resize.
10 <= value <= 1024Created
application/json- response
id*stringorganizationId*stringname*stringstatus*stringdeploying, running, stopped, failed, terminated, or interrupted; a row can also carry the queued status pending. interrupted means it served and is not serving now: it keeps its node, GPU and disk, and is not billed. A VM is interrupted while its node is offline and resumes when the node returns; a pod is interrupted for that and for losing its supervisor or its job on a live node, which the platform recovers by re-submitting it. After 7 days interrupted a VM is stopped (disk kept) and a pod is deleted.
statusReason?stringCause of the current status. The machine-readable values are "node_lost" (the host went offline and is expected back, so the disk is preserved), "node_removed" (the host was decommissioned, so the disk is gone and a restart deploys fresh elsewhere), "vm_exited", "user_stopped", "user_terminated", "user_restarted", "spot_reclaimed_by_capacity_owner", "spot_preempted_for_reservation_tenant", "auto_stopped_insufficient_balance", "auto_stopped_account_suspended", "auto_stopped_organization_deleted", the prefix "container failed to start: " followed by the container runtime's own message, and on a running workload the prefix "node leaving at " followed by the RFC 3339 time its host is removed, when the workload ends with node_removed. Anything else is free text for a human to read.
gpuModelId?stringgpuCount?integerendpointUrl?stringvolumeGb?integerpods only: size in GB of the pod's persistent /workspace volume.
public?booleantrue = open endpoint (no data-plane auth); false = require an org API key
createdAt?stringprovisioningStage?stringresourceSize?stringtier?stringdiskSizeGb?integergpuModelName?stringgpuManufacturer?stringGPU vendor from the catalog, the same value as GpuModel.manufacturer (NVIDIA, AMD); absent on a CPU-only row
pricePerHourCents?integerserviceType?stringmanaged-service tag; empty for a plain VM
managedBy?stringplatform owner of this row ("serving" = inference serving replica, read-only on customer surfaces); empty for a customer-launched VM
capabilityTier?stringcompute-fabric capability tier (e.g. "pod"); empty for a plain VM
computeClass?stringcompute-fabric purchase option (on_demand; pods created before the rename carry the legacy reserved or interruptible values); empty for a plain VM
imageUrl?stringtenant container image (pods)
curl -X POST 'https://api.openrelay.inc/v1/pods' \ -H 'Content-Type: application/json' \ -d '{ "name": "cuda-devbox", "gpuModelId": "rtx-4090", "computeClass": "on_demand", "image": "ghcr.io/acme/cuda-devbox:latest", "sshKeyIds": [ "3f1c7f2e-6a41-4a3f-9d0b-2c8e5a7b1d44" ], "httpPort": 8000, "envVars": { "HF_TOKEN": "hf_xxxxxxxx" }}'{ "id": "string", "organizationId": "string", "name": "string", "status": "string", "statusReason": "string", "gpuModelId": "string", "gpuCount": 0, "endpointUrl": "string", "volumeGb": 0, "public": true, "createdAt": "string", "provisioningStage": "string", "resourceSize": "string", "tier": "string", "diskSizeGb": 0, "gpuModelName": "string", "gpuManufacturer": "string", "pricePerHourCents": 0, "serviceType": "string", "managedBy": "string", "capabilityTier": "string", "computeClass": "string", "imageUrl": "string"}List an org's pods (cursor-paginated) GET
Returns the live compute-fabric pods of the org the API key resolves to as a cursor-paginated page of VmSummary items; terminated pods are left out. A pod is a managed_by=fabric Vm row (capabilityTier=pod), so each item is a VmSummary and paging mirrors listOrgVms.
Live pod GPU capacity and on-demand rates GET
Returns, per GPU model, the GPU units a pod create can claim right now, the largest single pod that fits, and the server-computed on-demand rate per GPU-hour. What a pod create can actually claim right now, per GPU model: free native (device-request) GPU units on online, non-delisted nodes a pod can be sized on (the exact filter admission uses), plus the SERVER-computed on-demand hourly rate. Only models with pod units and a price are returned; a create flow should offer exactly these and render these rates verbatim rather than re-deriving prices from the catalog.