Skip to content

Kubernetes API

Managed Kubernetes clusters — create, scale, secure, back up and upgrade clusters. Authenticate with a personal access token carrying the kubernetes:read or kubernetes:write scope (see API Keys).

Cluster creation and deletion are asynchronous: the API answers 202 Accepted and the cluster moves through provisioning_step values (validate → network → gateway → peering → cluster → wait → access → ptr → backup → activate) until status is running. Poll GET /v1/kubernetes/clusters/{cluster_id}.

Endpoints

All paths are relative to https://api.wayscloud.services/v1/kubernetes. {id} is the cluster id, {pool} a node-pool name, {ip} an IPv4 address, {backup} a backup id.

Catalogue

MethodPathDescription
GET/plansList cluster and node plans
GET/regionsRegions where Kubernetes is available
GET/versionsOffered Kubernetes versions
POST/estimateMonthly price estimate

Clusters

MethodPathDescription
GET/clustersList clusters
POST/clustersCreate a cluster (202)
GET/clusters/{id}Get a cluster
DELETE/clusters/{id}Delete a cluster (202)
GET/clusters/{id}/historyAudit history

Node pools

MethodPathDescription
POST/clusters/{id}/node-poolsAdd a node pool
POST/clusters/{id}/node-pools/{pool}/scaleScale a node pool
DELETE/clusters/{id}/node-pools/{pool}Delete a node pool

Access

MethodPathDescription
GET/clusters/{id}/kubeconfigAdmin kubeconfig (YAML)
PUT/clusters/{id}/api-accessSet the API allow-list

Public IPs

MethodPathDescription
POST/clusters/{id}/ipsAllocate a public IPv4
DELETE/clusters/{id}/ips/{ip}Release a public IPv4
PUT/clusters/{id}/ips/{ip}/ptrSet reverse DNS

Backups

MethodPathDescription
GET/clusters/{id}/backup-policyGet backup policy
PUT/clusters/{id}/backup-policySet backup policy
GET/clusters/{id}/backupsList backups
POST/clusters/{id}/backupsStart a backup now (202)
POST/clusters/{id}/backups/{backup}/restoreRestore a backup (202)

Upgrades

MethodPathDescription
GET/clusters/{id}/upgradeAvailable upgrades
POST/clusters/{id}/upgradeUpgrade Kubernetes version

Errors are returned as {"detail": {"code": "...", "message": "..."}}. Codes you will meet: not_found, not_ready (409), name_taken (409), invalid_plan, invalid_request, region_unavailable, ptr_not_forward_confirmed, capacity (503), backend_error (502).


GET /v1/kubernetes/plans

List cluster and node plans

Prices are per month in every supported currency; node plans also carry an hourly rate, which is what you are billed.

Query parameters: region (default no), kind (controlplane | node | ip | lb | storage | backup).

Example:

bash
curl "https://api.wayscloud.services/v1/kubernetes/plans?region=no&kind=node" \
  -H "Authorization: Bearer wayscloud_pat_abc12_YOUR_SECRET"

Response:

json
{
  "plans": [
    {
      "plan_code": "k8s-node-2c4g",
      "kind": "node",
      "name": "Node 2 vCPU / 4 GB",
      "cpu_cores": 2,
      "ram_mb": 4096,
      "disk_gb": 30,
      "prices": {"NOK": 249.0, "SEK": 249.0, "DKK": 179.0, "EUR": 29.0, "USD": 29.0},
      "hourly": {"NOK": 0.3458, "SEK": 0.3458, "DKK": 0.2486, "EUR": 0.0403, "USD": 0.0403}
    }
  ]
}

GET /v1/kubernetes/regions

Regions where Kubernetes is available

json
{"regions": [{"code": "no", "name": "Norge", "city": "Oslo", "country_code": "NO", "kubernetes_available": true, "price_multiplier": 1.0}]}

POST /v1/kubernetes/estimate

Monthly price estimate

Request Body:

FieldTypeDescription
plan_codestringCluster plan (k8s-cluster-dev, k8s-cluster-prod)
regionstringRegion code
node_poolsarray[{"name", "plan_code", "count"}]
currencystringNOK SEK DKK EUR USD

Response:

json
{
  "currency": "NOK",
  "region_multiplier": 1.0,
  "lines": [
    {"item": "k8s-cluster-dev", "quantity": 1, "monthly": 199.0},
    {"item": "k8s-node-2c4g", "quantity": 2, "monthly": 498.0}
  ],
  "monthly_total": 697.0
}

GET /v1/kubernetes/clusters

List clusters

Response:

FieldTypeDescription
clustersarrayCluster objects (see below)
totalinteger

POST /v1/kubernetes/clusters

Create a cluster — requires kubernetes:write. Returns 202 with the cluster in provisioning.

Request Body:

FieldTypeDescription
namestring2–63 chars, lowercase letters, digits, hyphens; unique per account
regionstringRegion code from /regions (default no)
plan_codestringk8s-cluster-dev (default) or k8s-cluster-prod
versionstringFrom /versions (default 1.34)
node_poolsarray1–8 pools: {"name", "plan_code", "count" (1–16), "labels"?, "taints"?}
api_ip_filterarrayCIDRs allowed to reach the Kubernetes API; empty = any source
ssh_key_idsarrayAccount SSH key ids to install on nodes (optional)

Example:

bash
curl -X POST https://api.wayscloud.services/v1/kubernetes/clusters \
  -H "Authorization: Bearer wayscloud_pat_abc12_YOUR_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "shop",
    "region": "no",
    "plan_code": "k8s-cluster-dev",
    "version": "1.34",
    "node_pools": [{"name": "default", "plan_code": "k8s-node-2c4g", "count": 2}],
    "api_ip_filter": ["203.0.113.0/24"]
  }'

Response (202):

json
{
  "id": "3f2b6c1e-8a4d-4f0e-9c21-7d5a1b2c3d4e",
  "name": "shop",
  "technical_name": "7f3a2c",
  "region": "no",
  "region_name": "Norge",
  "region_city": "Oslo",
  "plan_code": "k8s-cluster-dev",
  "version": "1.34",
  "status": "provisioning",
  "status_message": null,
  "provisioning_step": "validate",
  "api_endpoint": "https://7f3a2c.k8s.no.wayscloud.services:7443",
  "api_ip_filter": ["203.0.113.0/24"],
  "oidc_enabled": false,
  "network_cidr": "10.60.0.0/24",
  "node_pools": [
    {"id": "…", "name": "default", "plan_code": "k8s-node-2c4g", "count": 2, "status": "pending", "cpu_cores": 2, "ram_mb": 4096, "disk_gb": 30, "nodes_seen": 0}
  ],
  "public_ips": [],
  "created_at": "2026-09-16T10:12:00Z",
  "running_since": null,
  "trial_ends_at": "2026-09-30T10:12:00Z",
  "delete_requested_at": null
}

status values: provisioning, running, updating, upgrading, degraded, suspended, deleting, error.


GET /v1/kubernetes/clusters/

Get a cluster — the same object as above, with public_ips filled in once the cluster runs:

json
"public_ips": [
  {"id": "…", "address": "81.27.105.110", "family": "IPv4", "kind": "gateway", "included": true, "ptr_record": null},
  {"id": "…", "address": "212.147.234.88", "family": "IPv4", "kind": "floating", "included": false, "ptr_record": "7f3a2c.k8s.no.wayscloud.services"}
]

kind is gateway (outbound address, included in the cluster fee), lb (created by a Service of type LoadBalancer) or floating (ordered through /ips).


DELETE /v1/kubernetes/clusters/

Delete a cluster — requires kubernetes:write. A final backup is kept for 30 days.

Request Body: {"confirm_name": "shop"} — must equal the cluster name.

Response (202): the cluster with status: "deleting". It disappears from the list once the provider confirms removal.


POST /v1/kubernetes/clusters/{cluster_id}/node-pools

Add a node pool

json
{"name": "workers-large", "plan_code": "k8s-node-4c16g", "count": 3, "labels": {"tier": "batch"}, "taints": [{"key": "batch", "value": "true", "effect": "NoSchedule"}]}

POST /v1/kubernetes/clusters/{cluster_id}/node-pools/{pool_name}/scale

Scale a node pool{"count": 4} (0–16). Scaling down drains nodes first.

DELETE /v1/kubernetes/clusters/{cluster_id}/node-pools/

Delete a node pool — a cluster keeps at least one pool.


GET /v1/kubernetes/clusters/{cluster_id}/kubeconfig

Admin kubeconfig — requires kubernetes:write. Returns application/x-yaml, not JSON. Treat it as a secret; every download is recorded in the cluster history.

bash
curl https://api.wayscloud.services/v1/kubernetes/clusters/$CLUSTER/kubeconfig \
  -H "Authorization: Bearer wayscloud_pat_abc12_YOUR_SECRET" -o kubeconfig.yaml
chmod 600 kubeconfig.yaml && KUBECONFIG=kubeconfig.yaml kubectl get nodes

The file points at https://{id}.k8s.{region}.wayscloud.services:7443.

PUT /v1/kubernetes/clusters/{cluster_id}/api-access

Set the API allow-list{"cidrs": ["203.0.113.0/24", "198.51.100.7/32"]}; an empty list allows any source. Takes effect within a minute.


POST /v1/kubernetes/clusters/{cluster_id}/ips

Allocate a public IPv4 — billed per day. Use it in a Service annotation to pin a load balancer to the address.

DELETE /v1/kubernetes/clusters/{cluster_id}/ips/

Release a public IPv4 — only addresses of kind floating.

PUT /v1/kubernetes/clusters/{cluster_id}/ips/{address}/ptr

Set reverse DNS{"ptr_record": "mail.example.com"}. The name must already resolve to the address (ptr_not_forward_confirmed otherwise). Not available on the outbound gateway address.


GET /v1/kubernetes/clusters/{cluster_id}/backup-policy

json
{"enabled": true, "schedule_cron": "0 3 * * *", "retention_days": 14, "include_volumes": true, "namespaces": null, "active": true, "updated_at": "…"}

PUT /v1/kubernetes/clusters/{cluster_id}/backup-policy

Same fields as the response; retention_days 1–365, schedule_cron is a 5-field cron expression in the cluster's local time, namespaces null means every namespace you created.

GET /v1/kubernetes/clusters/{cluster_id}/backups

json
{"backups": [{"id": "…", "kind": "scheduled", "name": "daily-20260916030000", "status": "Completed", "started_at": "…", "completed_at": "…", "expires_at": "…", "items_count": 42, "error": null}]}

POST /v1/kubernetes/clusters/{cluster_id}/backups

Start a backup now{"namespaces": ["shop"]} or {} for all. Returns 202 {"name": "manual-20260916-101200", "status": "InProgress"}.

POST /v1/kubernetes/clusters/{cluster_id}/backups/{backup_id}/restore

Restore a backup into the same cluster — {"namespaces": ["shop"]} optional. Only Completed backups can be restored. Returns 202 {"restore": "restore-…", "status": "InProgress"}.


GET /v1/kubernetes/clusters/{cluster_id}/upgrade

Available upgrades{"versions": ["1.35"]}.

POST /v1/kubernetes/clusters/{cluster_id}/upgrade

Upgrade Kubernetes version{"version": "1.35"}. A backup is taken first; status is upgrading until done.


SDK and CLI

python
from wayscloud import WaysCloudClient
c = WaysCloudClient(token="wayscloud_pat_...")
cluster = c.kubernetes.create("shop", [{"name": "default", "plan_code": "k8s-node-2c4g", "count": 2}])
cluster = c.kubernetes.wait(cluster["id"])          # blocks until running
open("kubeconfig.yaml", "w").write(c.kubernetes.kubeconfig(cluster["id"]))
bash
cloud k8s create shop --pool k8s-node-2c4g:2 --wait
cloud k8s kubeconfig <cluster-id> -o ~/.kube/shop.yaml

See Python SDK and CLI.