Skip to content

Kubernetes Service API Reference

Reference · Updated Sep 2026

Coming from another cloud?

▸AWS·Amazon EKS Cluster

Amazon EKS Clusterhigh

  • EKS control plane fully AWS-managed, single-tenant, across 3 AZs with auto scale/replace; on Quake AI you run the control plane on Nova instances, provisioned through Magnum or self-managed with OpenTofu, and you operate it.
  • EKS regional API endpoint with SLA; Quake AI exposes the kube API via Neutron LB with floating IP.
  • EKS charges a per-hour cluster platform fee on top of the underlying compute; Quake AI charges only for underlying Nova/Neutron/Cinder resources with no K8s platform fee.
  • EKS managed nodes auto AMI updates, Spot integration; Quake AI self-managed nodes require manual OS image selection and update management.
AWS docs ↗
▸Azure·AKS Cluster

AKS Clusterhigh

  • Azure automatically provisions and manages the control plane at no additional cost (Free tier) or fixed fee (Standard tier with SLA), offloading health monitoring and upgrades; on Quake AI you provision a cluster through Magnum (openstack coe cluster create) or self-managed Kubernetes on Nova instances (OpenTofu plus kubeadm, k3s, or RKE2), and you operate the cluster after creation.
  • No OpenStack integration; uses Azure Resource Manager for cluster lifecycle.
  • Pre-configured with Azure-specific defaults and add-ons like application routing.
  • Managed via Azure Virtual Machine Scale Sets (VMSS) with auto-scaling and upgrades; Quake AI uses Nova instances provisioned via OpenTofu with user-managed scaling and upgrades.
Azure docs ↗
▸DigitalOcean·API

DigitalOcean APIhigh

  • Uses REST API over HTTPS with Bearer token authentication via personal access tokens, not OpenStack's Keystone token-based auth.
  • Base URL https://api.digitalocean.com/v2, incompatible with OpenStack APIs like Nova/Neutron.
  • Scoped permissions tied to granular API scopes based on team roles, unlike OpenStack role/project assignments.
  • Rate limits: 5000/hour, 250/minute.
DigitalOcean docs ↗
▸Google Cloud·GKE Cluster

GKE Clusterhigh

  • GKE provides Autopilot mode with fully managed node provisioning and scaling by Google; on Quake AI you provision clusters through Magnum or self-managed Kubernetes on Nova instances and manage node scaling yourself.
  • Control plane is fully managed with automatic upgrades through release channels; Quake AI requires user-provisioned and user-managed control plane nodes.
  • Cluster creation uses gcloud CLI vs OpenStack CLI (openstack coe cluster create).
  • Custom machine types, spot VMs, accelerators in node pools; Quake AI K8s nodes use standard Nova flavors.
Google Cloud docs ↗
▸Hetzner·Cloud API

Cloud APIhigh

  • Proprietary REST API over HTTPS with Bearer token auth, not OpenStack Identity API (keystone) endpoints or mechanisms.
  • Base URL https://api.hetzner.cloud/v1/ with resource-specific endpoints (e.g., /servers) vs OpenStack service endpoints (nova, cinder).
  • No multi-project handling in single auth; separate per-project tokens vs keystone scopes/projects.
  • Missing identity/catalog endpoints; no service discovery via API.
Hetzner docs ↗

Kubernetes service API reference

The Kubernetes API reference documents every Magnum endpoint with curl examples.

The Kubernetes service runs on the Container Infrastructure Management service (OpenStack Magnum). The HTTP API lives in the container-infra entry of the service catalog, not in the Compute (Nova) or Network (Neutron) catalogs.

In the paths below, {cluster_id}, {clustertemplate_id}, and {nodegroup_id} are placeholders. Replace each one with the UUID of the cluster, cluster template, or node group you are addressing. Request bodies use SCREAMING_SNAKE_CASE placeholders such as CLUSTER_NAME; replace each placeholder with a real value before sending the request.

Base URL and authentication#

All paths are relative to the container-infra service endpoint. Look the endpoint up in the service endpoints table for your region:

https://container-infra.{region}.rumble.cloud/v1

For example, the us-east-1 base URL is https://container-infra.us-east-1.rumble.cloud/v1.

Every request requires a Keystone token in the X-Auth-Token header. Requests with a body (POST and PATCH) also require Content-Type: application/json. To target a specific microversion, send the OpenStack-API-Version header; the service advertises a minimum of container-infra 1.1 and a maximum of container-infra 1.10.

X-Auth-Token: YOUR_TOKEN
Content-Type: application/json
OpenStack-API-Version: container-infra 1.10

Errors#

The service returns two error envelope shapes. Keystone rejects an unauthenticated request with its own envelope:

JSON
{
  "error": {
    "code": 401,
    "title": "Unauthorized",
    "message": "The request you have made requires authentication."
  }
}

Magnum reports validation and request errors with an errors array:

JSON
{
  "errors": [
    {
      "request_id": "",
      "code": "client",
      "status": 400,
      "title": "Invalid input for field/attribute cluster_template_id",
      "detail": "Invalid input for field/attribute cluster_template_id. Value: 'None'. Mandatory field missing.",
      "links": []
    }
  ]
}

Clusters#

List clusters#

bash
GET /v1/clusters

Returns 200. The response wraps the results in a clusters array:

JSON
{
  "clusters": []
}

Show cluster details#

bash
GET /v1/clusters/{cluster_id}

Returns 200 with the cluster object.

Create a cluster#

bash
POST /v1/clusters

Returns 201. Counts are JSON integers, not strings. The keypair field on a cluster is named differently from the keypair_id field on a cluster template; this asymmetry mirrors Magnum's own schema.

Request body:

JSON
{
  "cluster_template_id": "CLUSTER_TEMPLATE_ID",
  "name": "CLUSTER_NAME",
  "master_count": 1,
  "node_count": 3,
  "keypair": "KEYPAIR_NAME",
  "labels": {
    "kube_tag": "v1.24.16-rancher1"
  }
}

Update a cluster#

bash
PATCH /v1/clusters/{cluster_id}

Returns 202; the update runs asynchronously. The body is an RFC 6902 JSON-Patch document, which is an array of operations. Each value must match the target field's JSON type, so a node count is an integer.

Request body:

JSON
[
  {
    "op": "replace",
    "path": "/node_count",
    "value": 5
  }
]

Delete a cluster#

bash
DELETE /v1/clusters/{cluster_id}

Returns 204.

Cluster templates#

List cluster templates#

bash
GET /v1/clustertemplates

Returns 200. The response wraps the results in a clustertemplates array:

JSON
{
  "clustertemplates": [
    {
      "uuid": "2364d8e3-c4d7-469d-90f1-85664a39de83",
      "name": "Standard-v2.0-k8s-calico-fc38_v1.24.16",
      "coe": "kubernetes",
      "image_id": "FedoraCoreOS-38",
      "flavor_id": "m2a.2xlarge",
      "master_flavor_id": "m2a.xlarge",
      "dns_nameserver": "1.1.1.1,8.8.8.8",
      "keypair_id": null,
      "external_network_id": "PublicStatic",
      "network_driver": "calico",
      "docker_volume_size": 50,
      "volume_driver": "cinder",
      "cluster_distro": "fedora-coreos",
      "labels": {
        "kube_tag": "v1.24.16-rancher1"
      },
      "public": true,
      "server_type": "vm",
      "created_at": "2026-06-04T00:00:00+00:00",
      "updated_at": null
    }
  ]
}

Show cluster template details#

bash
GET /v1/clustertemplates/{clustertemplate_id}

Returns 200 with the cluster template object.

Create a cluster template#

bash
POST /v1/clustertemplates

Returns 201. docker_volume_size is a JSON integer in GB, not a string.

Request body:

JSON
{
  "name": "TEMPLATE_NAME",
  "image_id": "IMAGE_NAME_OR_ID",
  "keypair_id": "KEYPAIR_NAME",
  "external_network_id": "EXTERNAL_NETWORK_NAME_OR_ID",
  "dns_nameserver": "1.1.1.1,8.8.8.8",
  "flavor_id": "FLAVOR_NAME",
  "master_flavor_id": "FLAVOR_NAME",
  "docker_volume_size": 50,
  "network_driver": "calico",
  "volume_driver": "cinder",
  "coe": "kubernetes"
}

Update a cluster template#

bash
PATCH /v1/clustertemplates/{clustertemplate_id}

Returns 200. The body is an RFC 6902 JSON-Patch array. Each value must match the target field's JSON type, so docker_volume_size is an integer.

Request body:

JSON
[
  {
    "op": "replace",
    "path": "/docker_volume_size",
    "value": 60
  }
]

The supported operations are add, replace, and remove.

Delete a cluster template#

bash
DELETE /v1/clustertemplates/{clustertemplate_id}

Returns 204.

Node groups#

Node groups manage the worker pools of a cluster. The service nests them under a cluster, so every path carries a {cluster_id}.

List node groups#

bash
GET /v1/clusters/{cluster_id}/nodegroups

Show node group details#

bash
GET /v1/clusters/{cluster_id}/nodegroups/{nodegroup_id}

Create a node group#

bash
POST /v1/clusters/{cluster_id}/nodegroups

Request body:

JSON
{
  "name": "NODEGROUP_NAME",
  "node_count": 2,
  "flavor_id": "FLAVOR_NAME",
  "role": "worker"
}

Update a node group#

bash
PATCH /v1/clusters/{cluster_id}/nodegroups/{nodegroup_id}

The body is an RFC 6902 JSON-Patch array:

JSON
[
  {
    "op": "replace",
    "path": "/node_count",
    "value": 4
  }
]

Delete a node group#

bash
DELETE /v1/clusters/{cluster_id}/nodegroups/{nodegroup_id}

Certificates#

The certificates endpoints back the cluster CA workflow (show, sign, and rotate).

Show the cluster CA certificate#

bash
GET /v1/certificates/{cluster_id}

Sign a certificate#

bash
POST /v1/certificates

Send a PEM certificate signing request for the cluster:

JSON
{
  "cluster_uuid": "CLUSTER_ID",
  "csr": "CERTIFICATE_SIGNING_REQUEST_PEM"
}

Rotate the cluster CA certificate#

bash
PATCH /v1/certificates/{cluster_id}

Quotas#

Show project quotas#

bash
GET /v1/quotas

Show one resource quota#

bash
GET /v1/quotas/{project_id}/{resource}

Set a quota#

bash
POST /v1/quotas

Request body:

JSON
{
  "project_id": "PROJECT_ID",
  "resource": "Cluster",
  "hard_limit": 10
}

Stats#

Show cluster statistics#

bash
GET /v1/stats

Quick answers

Was this page helpful?