> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rafftechnologies.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Kubernetes Cluster

> Create a managed Kubernetes cluster. Provisioning is asynchronous — the response returns immediately with `pending` status; the cluster is usable when `ready` is `true` (typically 8–12 minutes).

API-created clusters follow the account's billing mode: pay-as-you-go accounts bill per node-hour; subscription accounts are billed monthly — the API validates the saved payment method, creates the subscription, and charges it automatically (same behavior as VM creation).

Retries are safe with an `Idempotency-Key` header: a repeated request with the same key returns the cluster created by the first request instead of provisioning a second one.


<sub>Updated August 24, 2026</sub>

<Note>
  Before creating a cluster, you need:

  * A **node plan ID** — get one from [List Kubernetes Node Plans](/api-reference/kubernetes/list-node-plans)
  * A **project ID** for the `X-Project-ID` header — see [Generate an API key](/products/manage/team-projects/quickstart-guides/generate-api-key)
  * Optionally a **version ID** from [List Kubernetes Versions](/api-reference/kubernetes/list-kubernetes-versions) (omit for the default)

  API-created clusters bill **pay-as-you-go** per node-hour. Accounts on subscription billing receive `402` — create the cluster from the dashboard instead. Retries are safe with an `Idempotency-Key` header.
</Note>


## OpenAPI

````yaml POST /api/v1/k8s/clusters
openapi: 3.0.3
info:
  title: Raff API
  description: >
    REST API for managing cloud infrastructure on Raff.


    ## Authentication

    Most endpoints require authentication via API key. Catalog endpoints under
    `/api/v1/public/` are open and require no authentication.


    ### API Key Authentication

    Include your API key in the `X-API-Key` header:
      ```
      curl -H "X-API-Key: YOUR_API_KEY" https://api.rafftechnologies.com/api/v1/vms
      ```

    ## Catalog

    Use the public catalog endpoints to discover available regions, OS
    templates, and pricing plans before creating resources:

    - `GET /api/v1/public/regions` — list available regions

    - `GET /api/v1/public/templates` — list OS templates (use the `id` as
    `template_id` when creating a VM)

    - `GET /api/v1/public/pricing/vm` — list VM pricing plans (use the `id` as
    `pricing_id` when creating a VM)

    - `GET /api/v1/public/pricing/volume` — volume storage pricing

    - `GET /api/v1/public/pricing/snapshot` — snapshot storage pricing

    - `GET /api/v1/public/pricing/backup` — backup storage pricing

    - `GET /api/v1/public/pricing/ip` — IP address pricing

    - `GET /api/v1/public/pricing/database` — managed database pricing (plans,
    high availability, read replicas, extra storage)

    - `GET /api/v1/public/pricing/kubernetes` — managed Kubernetes pricing
    (worker node plans, HA control plane, storage nodes)
  version: 1.0.0
  contact:
    name: Raff Technologies
    url: https://rafftechnologies.com
servers:
  - url: https://api.rafftechnologies.com
    description: Production
security:
  - ApiKeyAuth: []
tags:
  - name: Catalog
    description: >-
      Discover available regions, OS templates, and pricing plans. No
      authentication required.
  - name: Health
    description: Health check endpoints
  - name: Projects
    description: Organize resources into projects for billing and access control
  - name: Virtual Machines
  - name: Kubernetes
    description: >-
      Managed Kubernetes clusters — HA control planes, autoscaling node pools,
      kubeconfig access
  - name: Functions
    description: Deploy and manage serverless functions
  - name: Raff Apps
    description: >-
      Push-to-deploy web services, private services, workers, cron jobs, and
      one-off jobs on the Raff PaaS — with custom domains, environment
      variables, autoscaling, and per-second billing with spend caps
  - name: Managed Databases
    description: >-
      Fully managed PostgreSQL and Valkey databases with private VPC networking,
      automatic backups, and metrics
  - name: Networking
    description: >-
      Attach and detach VPCs, floating IPs, and security groups to VM network
      interfaces
  - name: Snapshots
    description: Point-in-time copies of VMs and volumes for quick rollback or cloning
  - name: Backups
    description: Scheduled and on-demand VM backups with restore capability
  - name: Backup Schedules
    description: Recurring daily or weekly backup schedules attached to a VM
  - name: SSH Keys
    description: Manage account-level SSH keys for VM provisioning
  - name: Members
    description: Account-level members — invite, list, update role, remove
  - name: Project Members
    description: Members of a specific project — same model as Members but project-scoped
  - name: Roles
    description: Custom roles bundling account or project permissions
  - name: Permissions
    description: List the catalog of permission strings used by roles
  - name: API Keys
    description: Create and manage API keys for programmatic access
  - name: Invitations
    description: Create and cancel email-based invitations to join the account or a project
paths:
  /api/v1/k8s/clusters:
    post:
      tags:
        - Kubernetes
      summary: Create Kubernetes cluster
      description: >
        Create a managed Kubernetes cluster. Provisioning is asynchronous — the
        response returns immediately with `pending` status; the cluster is
        usable when `ready` is `true` (typically 8–12 minutes).


        API-created clusters follow the account's billing mode: pay-as-you-go
        accounts bill per node-hour; subscription accounts are billed monthly —
        the API validates the saved payment method, creates the subscription,
        and charges it automatically (same behavior as VM creation).


        Retries are safe with an `Idempotency-Key` header: a repeated request
        with the same key returns the cluster created by the first request
        instead of provisioning a second one.
      operationId: createK8sCluster
      parameters:
        - $ref: '#/components/parameters/ProjectIDHeader'
        - name: Idempotency-Key
          in: header
          required: false
          description: >-
            Any unique string (max 128 chars). Reuse the same value when
            retrying a failed or timed-out request.
          schema:
            type: string
            maxLength: 128
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateK8sClusterRequest'
      responses:
        '201':
          description: Cluster accepted for provisioning (status `pending`)
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  cluster:
                    $ref: '#/components/schemas/K8sCluster'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientBalance'
components:
  parameters:
    ProjectIDHeader:
      name: X-Project-ID
      in: header
      required: true
      description: >-
        Project ID. Required for all mutating operations (create, delete, power
        actions, resize).
      schema:
        type: string
        format: uuid
  schemas:
    CreateK8sClusterRequest:
      type: object
      required:
        - name
        - node_pools
      properties:
        name:
          type: string
          description: 1–63 lowercase letters, digits or hyphens; unique within the account
          example: prod
        k8s_version_id:
          type: integer
          description: >-
            From [List Kubernetes
            versions](#tag/Kubernetes/operation/listK8sVersions). Omit for the
            default version.
          example: 1
        node_pools:
          type: array
          minItems: 1
          description: At least one pool with at least 2 nodes in total
          items:
            $ref: '#/components/schemas/K8sNodePoolInput'
        ha_enabled:
          type: boolean
          description: 3-master highly available control plane (flat monthly fee)
          default: false
        firewall_enabled:
          type: boolean
          description: Attach the platform baseline firewall to the public gateway
          default: true
        firewall_security_group_id:
          type: string
          format: uuid
          description: Use one of your security groups instead of the baseline
        storage_node_count:
          type: integer
          enum:
            - 0
            - 2
            - 3
          description: >-
            Dedicated Longhorn storage nodes (0 disables in-cluster block
            storage)
          default: 0
        storage_node_disk_gb:
          type: integer
          description: >-
            Data disk per storage node; required when `storage_node_count` > 0.
            Bounds from [List Kubernetes node
            plans](#tag/Kubernetes/operation/listK8sNodePlans)
            `storage_pricing`.
          example: 100
        traefik_enabled:
          type: boolean
          description: Install the Traefik ingress controller
          default: false
        metallb_enabled:
          type: boolean
          description: Install MetalLB for `LoadBalancer` services
          default: false
        cluster_cidr:
          type: string
          description: Pod network override (private IPv4, /12–/24)
          default: 10.42.0.0/16
        service_cidr:
          type: string
          description: Service network override (private IPv4, /12–/24)
          default: 10.43.0.0/16
        region:
          type: string
          enum:
            - us-east
          default: us-east
    K8sCluster:
      type: object
      required:
        - id
        - cluster_id
        - name
        - status
        - ready
      properties:
        id:
          type: string
          format: uuid
          description: Internal UUID
        cluster_id:
          type: string
          description: Short cluster ID used in URLs and the API endpoint hostname
          example: q7z7zg
        name:
          type: string
          example: prod
        status:
          type: string
          enum:
            - pending
            - deploying
            - running
            - warning
            - failed
            - deleting
            - delete_failed
            - deleted
          description: >
            - `pending` / `deploying` — provisioning

            - `running` — control plane up

            - `warning` — degraded (see `status_message`)

            - `failed` — provisioning failed; infrastructure was torn down (see
            `status_message`)

            - `deleting` — teardown in progress

            - `delete_failed` — teardown incomplete; nothing removed — retry the
            delete
        status_message:
          type: string
          description: Human-readable detail for warning/failed/delete_failed
        ready:
          type: boolean
          description: >-
            `true` when the cluster is running and no node pool is mid-operation
            — safe to use without interpreting `status`
        dns_domain:
          type: string
          example: q7z7zg.k8s.raffusercloud.com
        api_endpoint:
          type: string
          example: https://q7z7zg.k8s.raffusercloud.com:6443
        k8s_version:
          type: string
          example: v1.33.4+rke2r1
        k8s_version_id:
          type: integer
        region:
          type: string
          enum:
            - us-east
        master_count:
          type: integer
          description: 1, or 3 with an HA control plane
        worker_count:
          type: integer
          description: Total worker nodes across all pools
        ha_enabled:
          type: boolean
        firewall_enabled:
          type: boolean
        longhorn_enabled:
          type: boolean
          description: Dedicated storage nodes with Longhorn block storage
        traefik_enabled:
          type: boolean
        metallb_enabled:
          type: boolean
        billing_type:
          type: string
          enum:
            - payg
            - subscription
        project_id:
          type: string
          format: uuid
        price_per_hour:
          type: number
          format: double
          description: >-
            Current hourly price (workers + storage + HA fee; control plane is
            free)
        price_per_month:
          type: number
          format: double
        vpc_id:
          type: string
          description: Dedicated VPC the cluster runs in
        private_vip:
          type: string
          description: Cluster gateway IP inside the VPC
          example: 10.1.0.254
        node_pools:
          type: array
          items:
            $ref: '#/components/schemas/K8sNodePool'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    K8sNodePoolInput:
      type: object
      required:
        - name
        - node_count
        - plan_id
      properties:
        name:
          type: string
          description: 1–63 lowercase letters, digits or hyphens; unique within the cluster
          example: default-pool
        node_count:
          type: integer
          minimum: 2
          maximum: 20
          example: 3
        plan_id:
          type: integer
          description: >-
            Worker plan from [List Kubernetes node
            plans](#tag/Kubernetes/operation/listK8sNodePlans)
          example: 11
        min_nodes:
          type: integer
          description: Autoscaler floor
        max_nodes:
          type: integer
          description: Autoscaler ceiling
        autoscale_enabled:
          type: boolean
          default: false
        labels:
          type: string
          description: >-
            JSON object of Kubernetes labels applied to the pool's nodes, e.g.
            `{"workload":"web"}`
        taints:
          type: string
          description: >-
            JSON array of taints, e.g.
            `[{"key":"gpu","value":"true","effect":"NoSchedule"}]`
    K8sNodePool:
      type: object
      required:
        - id
        - name
        - node_count
        - status
      properties:
        id:
          type: string
          format: uuid
        cluster_id:
          type: string
          format: uuid
        name:
          type: string
        node_count:
          type: integer
          description: Target node count
        actual_node_count:
          type: integer
          description: Nodes that currently exist (differs from `node_count` while scaling)
        min_nodes:
          type: integer
        max_nodes:
          type: integer
        autoscale_enabled:
          type: boolean
        plan_id:
          type: integer
        cpu:
          type: integer
        ram:
          type: integer
          description: GB
        disk:
          type: integer
          description: GB
        labels:
          type: string
        taints:
          type: string
        status:
          type: string
          enum:
            - pending
            - provisioning
            - running
            - scaling
            - failed
            - deleting
            - deleted
          description: >-
            `scaling`/`deleting` are asynchronous — watch `status_message` for
            the outcome (e.g. a drain blocked by a PodDisruptionBudget)
        status_message:
          type: string
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/K8sNode'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Error:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
    K8sNode:
      type: object
      properties:
        id:
          type: string
          format: uuid
        pool_id:
          type: string
          format: uuid
        cluster_id:
          type: string
          format: uuid
        name:
          type: string
        role:
          type: string
          enum:
            - vnf
            - master
            - worker
            - storage
          description: '`vnf` is the cluster''s network gateway'
        status:
          type: string
          enum:
            - pending
            - provisioning
            - running
            - not_ready
            - cordoned
            - failed
            - deleting
            - deleted
          description: >-
            `running` means the kubelet reports Ready; `cordoned` =
            unschedulable (drain or manual cordon); `not_ready` = joined but
            failing health checks
        cpu:
          type: integer
        ram:
          type: integer
          description: GB
        disk:
          type: integer
          description: GB
        ip_address:
          type: string
        price_per_hour:
          type: number
          format: double
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
  responses:
    BadRequest:
      description: Invalid request parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InsufficientBalance:
      description: >
        Account balance is insufficient for this operation. Top up the balance
        and retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Payment Required
            message: Insufficient balance for subscription
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key for authentication. Each key is bound to a specific account.

````