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

# Create a Virtual GPU cluster

export const MethodSection = ({children}) => children ?? null;

export const MethodSwitch = ({children}) => {
  const tabs = React.Children.toArray(children).map(c => {
    if (!c || !c.props) return null;
    if (c.props.id) return c;
    const inner = c.props.children;
    if (inner && inner.props && inner.props.id) return inner;
    return null;
  }).filter(Boolean);
  const firstId = tabs.length > 0 ? tabs[0].props.id : "";
  const [active, setActive] = React.useState(firstId);
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem("gcore_docs_method");
      if (saved && tabs.find(t => t.props.id === saved)) {
        setActive(saved);
      }
    } catch (_) {}
  }, []);
  React.useEffect(() => {
    try {
      document.querySelectorAll("h2[id], h3[id]").forEach(heading => {
        const visible = heading.offsetParent !== null;
        document.querySelectorAll(`a[href="#${heading.id}"]`).forEach(link => {
          if (link.closest("h1,h2,h3,h4,h5,h6")) return;
          const li = link.closest("li");
          if (li) li.style.display = visible ? "" : "none";
        });
      });
    } catch (_) {}
    window.dispatchEvent(new Event("scroll"));
  }, [active]);
  const handleClick = id => {
    setActive(id);
    try {
      localStorage.setItem("gcore_docs_method", id);
    } catch (_) {}
  };
  return <div>
      <div className="not-prose flex gap-0 border-b border-zinc-200 dark:border-zinc-800 mb-8 mt-2" role="tablist">
        {tabs.map(tab => {
    const isActive = active === tab.props.id;
    return <button key={tab.props.id} role="tab" aria-selected={isActive} onClick={() => handleClick(tab.props.id)} className={["px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors cursor-pointer", isActive ? "border-primary text-primary" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"].join(" ")}>
              {tab.props.label}
            </button>;
  })}
      </div>

      {tabs.map(tab => <div key={tab.props.id} style={{
    display: active === tab.props.id ? "" : "none"
  }}>
          {tab.props.children}
        </div>)}
    </div>;
};

<MethodSwitch>
  <MethodSection id="portal" label="Customer Portal">
    <p>A Virtual GPU cluster is a group of one or more virtual machines with dedicated GPU access. It provides a managed environment for AI training, inference, and other GPU workloads without provisioning bare metal servers. Virtual GPU is one of the [cluster types](/edge-ai/ai-infrastructure/about-gpu-cloud) available in GPU Cloud, alongside [Bare Metal](/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster) and [Spot](/edge-ai/ai-infrastructure/spot-bare-metal-gpu) options.</p>

    <Info>
      An SSH key must be added to the project before creating a cluster — it's required to connect to nodes. GPU model availability varies by region.
    </Info>

    ## Cluster architecture

    <p>Each Virtual GPU cluster consists of one or more virtual machine nodes. All nodes are created from an identical template (image, network settings, disk configuration). After creation, individual nodes can have their disk and network configurations modified independently.</p>

    <p>For flavors with InfiniBand support, high-speed inter-node networking is configured automatically. This enables efficient distributed training across multiple nodes without manual network configuration.</p>

    <p>Each node has:</p>

    * A **network boot disk** (required). At least one network disk is required as the boot volume for the operating system.
    * A **local data disk** added by default. This non-replicated disk is dedicated to temporary storage.
    * Optional **network data disks** that can be attached during creation or added later. Network disks persist independently of node state.
    * Optional **file share** integration for shared storage across instances.

    <Warning>
      The local data disk is a non-replicated volume that comes with every Virtual GPU instance. This disk:

      * Cannot be modified, detached, or used as a boot volume
      * Is strictly bound to the specific virtual machine and its configuration
      * Is wiped when the node is reconfigured, powered off (shelved), or deleted

      Use this disk only for temporary data. Store all important data on network disks or NFS storage to prevent data loss.
    </Warning>

    ## Create a Virtual GPU cluster

    <p>In the [Gcore Customer Portal](https://portal.gcore.com), navigate to **GPU Cloud**, select the target region, open **GPU Clusters** > **Virtual GPU Clusters**, and click **Create Cluster**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/virtual-gpu-clusters-list.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=892a30535980d15547655a61e58e7cee" alt="Virtual GPU Clusters page with cluster list and Create Cluster button" width="1024" height="369" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/virtual-gpu-clusters-list.png" />
    </Frame>

    <Steps>
      <Step title="Configure cluster capacity">
        Select the GPU model and flavor for each cluster node.

        1. Select the **GPU Model**. Available models depend on the region.
        2. Enable or disable **Show out of stock** to filter available flavors.
        3. Select a flavor. Each flavor card displays GPU configuration, vCPU count, RAM capacity, and pricing.

        <Frame>
          <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/cluster-capacity-config.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=a790bce2d5f914751814f1665688013e" alt="Cluster capacity section with GPU cluster type selection and available flavors" width="1024" height="463" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/cluster-capacity-config.png" />
        </Frame>
      </Step>

      <Step title="Set the number of instances">
        In the **Number of Instances** section, specify how many virtual machines to provision. Each instance has the selected flavor configuration; the minimum is one, the maximum depends on regional availability.

        <Frame>
          <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/number-of-instances.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=19aadffca7ecc79cca4057d2f0920d7f" alt="Number of Instances section with plus/minus controls and selected flavor summary" width="928" height="149" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/number-of-instances.png" />
        </Frame>
      </Step>

      <Step title="Select image">
        The image defines the operating system and pre-installed software for cluster nodes.

        Select the operating system in the **Image** section:

        * **Public**: Pre-configured images with NVIDIA drivers and CUDA toolkit
        * **Custom**: Custom images uploaded to the account

        Images with the `eni` suffix are configured for InfiniBand interconnect.

        <Frame>
          <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/image-selector.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=f361eb04458397b0aee62dc1e7c49693" alt="Image section with Public and Custom tabs and Ubuntu image dropdown" width="658" height="180" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/image-selector.png" />
        </Frame>
      </Step>

      <Step title="Configure volumes">
        Each instance includes a **System volume** (required boot disk), a **Local volume** (temporary non-replicated storage, added automatically), and optional **Additional network volumes**.

        <Frame>
          <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/volumes-config.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=02493f010c5ed4c7c744e0aae24c44a9" alt="Volumes section with System and Local volumes configuration" width="768" height="355" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/volumes-config.png" />
        </Frame>

        Configure the **System** volume:

        * **Name**: Enter a name for the volume
        * **Size**: Minimum size depends on the selected image (default: 120 GiB)
        * **Type**: Select from available storage types in the region

        To add additional storage, click **Add Volume**. All configured volumes are attached to every instance in the cluster.

        <Warning>
          Store important data on network volumes or file shares, not on the local volume. The local volume is wiped when the instance is shelved, reconfigured, or deleted, resulting in permanent data loss.
        </Warning>
      </Step>

      <Step title="Configure file share integration (optional)">
        The **File share integration** section connects [file shares](/cloud/file-shares/configure-file-shares) to all instances, making shared storage accessible across nodes. Use it for distributed training, shared datasets, or any workload that reads the same files from multiple nodes.

        1. Enable the **File Share integration** toggle.
        2. Select an existing file share or create a new one.

        <Frame>
          <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/file-share-integration.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=6ce57768c9544fc337dca7e5d2e4ab16" alt="File share integration section with toggle and file share selector" width="877" height="323" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/file-share-integration.png" />
        </Frame>
      </Step>

      <Step title="Configure network settings">
        Network settings control how cluster nodes access external services and other cloud resources. At least one interface is required.

        <Info>
          Virtual GPU clusters support a maximum of three network interfaces (Public and Private combined) per instance. If file share integration is enabled, the limit is reduced to two interfaces, because one interface is reserved for NFS connectivity. InfiniBand interfaces are not counted toward this limit.
        </Info>

        In the **Network settings** section, select the [interface type](/cloud/networking/create-and-manage-a-network) for cluster nodes:

        | Type             | Access                                        | Use case                                              |
        | ---------------- | --------------------------------------------- | ----------------------------------------------------- |
        | Public           | Direct internet access with dynamic public IP | Development, testing, quick access to cluster         |
        | Private          | Internal network only, no external access     | Production workloads, security-sensitive environments |
        | Dedicated public | Reserved static public IP                     | Production APIs, services requiring stable endpoints  |

        To add more interfaces, click **Add Interface**. Each interface can be expanded to configure [security groups](/cloud/networking/add-and-configure-a-firewall). The `default` security group is pre-selected.

        <Frame>
          <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/network-settings-security-groups.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=f9e653d500d0804bdb06411ea07fcf1e" alt="Security Groups field with default group selected per interface" width="844" height="687" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/network-settings-security-groups.png" />
        </Frame>
      </Step>

      <Step title="Configure SSH key">
        In the **SSH key** section, select an existing key from the dropdown or create a new one. If generating a new key pair, save the private key immediately as it cannot be retrieved later.

        <Frame>
          <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/ssh-key.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=1bd002d100c3c0a9a93987f9796991d5" alt="SSH key section with key dropdown selector" width="941" height="236" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/ssh-key.png" />
        </Frame>
      </Step>

      <Step title="Set additional options">
        The **Additional options** section provides optional settings: user data scripts for automated configuration and metadata tags for resource organization.

        <Frame>
          <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/additional-options.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=d355650af3fc2f8b53a122f534728fe8" alt="Additional options section with User data and Add tags toggles" width="1024" height="302" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/additional-options.png" />
        </Frame>
      </Step>

      <Step title="Name and create the cluster">
        In the **GPU Cluster Name** section, enter a name or use the auto-generated one. Review the estimated cost in the right panel, then click **Create Cluster**.

        The cluster appears in the list with **Creating** status. Provisioning typically takes 3–8 minutes.

        <Frame>
          <img src="https://mintcdn.com/gcore/W9hLxFUXyZVjy-WH/images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/cluster-creating-status.png?fit=max&auto=format&n=W9hLxFUXyZVjy-WH&q=85&s=7a4b953fccdb4a5b0395aa559cd371f3" alt="Virtual GPU Clusters list showing a newly created cluster with Creating status" width="1024" height="327" data-path="images/docs/edge-ai/ai-infrastructure/create-a-virtual-gpu-cluster/cluster-creating-status.png" />
        </Frame>

        Once all instances reach **Power on** status, the cluster is ready for use.

        <Warning>
          Cluster-level settings (image, default networks) cannot be changed after creation. New nodes added via scaling inherit the original configuration. To change these settings, create a new cluster.
        </Warning>
      </Step>
    </Steps>

    ## Connect to the cluster

    <p>After the cluster is created, connect to a node via SSH and verify that GPUs are available.</p>

    <Steps>
      <Step title="Connect via SSH">
        Open a terminal and connect using the default username `ubuntu`:

        ```bash theme={null}
        ssh ubuntu@<node-ip-address>
        ```

        Replace `<node-ip-address>` with the public or floating IP shown in the cluster details.
      </Step>

      <Step title="Verify GPU detection">
        ```bash theme={null}
        nvidia-smi
        ```

        A successful output shows the list of available GPUs, driver version, and CUDA version. If the command fails or no GPUs appear, the image may be missing the correct drivers.
      </Step>
    </Steps>

    <p>For nodes with only private interfaces, connect through a bastion host, VPN, or the Customer Portal [web console](/cloud/virtual-instances/connect/connect-to-your-instance-via-control-panel).</p>

    <p>Clusters can also be created programmatically using the [REST API](/api-reference/cloud/gpu-virtual/create-virtual-gpu-cluster).</p>
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>Create a virtual GPU cluster by selecting a flavor and image, then provisioning the cluster with a single API call.</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required, along with a
      [project ID](/api-reference/cloud/projects/list-projects)
      and a [region ID](/api-reference/cloud/regions/list-regions).
      Virtual GPU clusters are available in select regions — confirm availability with the [List flavors](#step-1-list-available-flavors) call before creating a cluster.
    </Info>

    <p>Open a terminal and set these environment variables before running the examples:</p>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    export GCORE_CLOUD_PROJECT_ID="{YOUR_PROJECT_ID}"
    export GCORE_CLOUD_REGION_ID="{YOUR_REGION_ID}"
    export GCORE_SSH_KEY_NAME="{YOUR_SSH_KEY_NAME}"
    ```

    ## Quickstart

    <p>Complete scripts that list flavors and images, create a one-node cluster, wait for provisioning, and print the SSH command.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        import time
        from gcore import Gcore

        client = Gcore()
        ssh_key_name = os.environ["GCORE_SSH_KEY_NAME"]

        # Step 1. List flavors — select the one with the fewest GPUs
        flavors = client.cloud.gpu_virtual.clusters.flavors.list()
        flavor = min(
            flavors.results,
            key=lambda f: f.hardware_properties.gpu_count,
        )
        print(f"Flavor: {flavor.name}  ({flavor.hardware_properties.gpu_count} GPU(s))")

        # Step 2. List images — select the first (latest) image
        images = client.cloud.gpu_virtual.clusters.images.list()
        image = images.results[0]
        print(f"Image: {image.name}")

        # Step 3. Create the cluster
        task_id_list = client.cloud.gpu_virtual.clusters.create(
            name="my-gpu-cluster",
            flavor=flavor.name,
            servers_count=1,
            servers_settings={
                "interfaces": [{"type": "external"}],
                "volumes": [{
                    "boot_index": 0,
                    "name": "boot-disk",
                    "size": 50,
                    "source": "image",
                    "type": "ssd_hiiops",
                    "image_id": image.id,
                }],
                "credentials": {"ssh_key_name": ssh_key_name},
            },
        )
        task_id = task_id_list.tasks[0]
        print(f"Provisioning — task: {task_id}")

        # Step 4. Poll task until FINISHED
        while True:
            task = client.cloud.tasks.get(task_id)
            if task.state == "FINISHED":
                cluster_id = task.created_resources.ai_clusters[0]
                print(f"Cluster ID: {cluster_id}")
                break
            if task.state == "ERROR":
                raise RuntimeError(f"Cluster creation failed: {task.error}")
            print(f"  state={task.state} — retrying in 10s…")
            time.sleep(10)

        # Step 5. Get the public IP and print SSH command
        ifaces = client.cloud.gpu_virtual.clusters.interfaces.list(cluster_id=cluster_id)
        for iface in ifaces.results:
            if iface.network.external:
                ip = iface.ip_assignments[0].ip_address
                print(f"SSH: ssh ubuntu@{ip}")
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"
            "sort"
            "time"

            "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/cloud"
        )

        func main() {
            sshKeyName := os.Getenv("GCORE_SSH_KEY_NAME")

            client := gcore.NewClient()
            ctx := context.Background()

            // Step 1. List flavors — select the one with fewest GPUs
            flavorList, err := client.Cloud.GPUVirtual.Clusters.Flavors.List(ctx,
                cloud.GPUVirtualClusterFlavorListParams{})
            if err != nil {
                panic(err)
            }
            sort.Slice(flavorList.Results, func(i, j int) bool {
                return flavorList.Results[i].HardwareProperties.GPUCount <
                    flavorList.Results[j].HardwareProperties.GPUCount
            })
            flavor := flavorList.Results[0]
            fmt.Printf("Flavor: %s  (%d GPU(s))\n", flavor.Name, flavor.HardwareProperties.GPUCount)

            // Step 2. List images — select the first (latest) image
            imageList, err := client.Cloud.GPUVirtual.Clusters.Images.List(ctx,
                cloud.GPUVirtualClusterImageListParams{})
            if err != nil {
                panic(err)
            }
            image := imageList.Results[0]
            fmt.Printf("Image: %s\n", image.Name)

            // Step 3. Create the cluster
            taskIDList, err := client.Cloud.GPUVirtual.Clusters.New(ctx,
                cloud.GPUVirtualClusterNewParams{
                    Name:         "my-gpu-cluster",
                    Flavor:       flavor.Name,
                    ServersCount: 1,
                    ServersSettings: cloud.GPUVirtualClusterNewParamsServersSettings{
                        Interfaces: []cloud.GPUVirtualClusterNewParamsServersSettingsInterfaceUnion{{
                            OfExternal: &cloud.GPUVirtualClusterNewParamsServersSettingsInterfaceExternal{},
                        }},
                        Volumes: []cloud.GPUVirtualClusterNewParamsServersSettingsVolumeUnion{{
                            OfImage: &cloud.GPUVirtualClusterNewParamsServersSettingsVolumeImage{
                                BootIndex: int64(0),
                                Name:      "boot-disk",
                                Size:      int64(50),
                                Type:      "ssd_hiiops",
                                ImageID:   image.ID,
                            },
                        }},
                        Credentials: cloud.GPUVirtualClusterNewParamsServersSettingsCredentials{
                            SSHKeyName: gcore.String(sshKeyName),
                        },
                    },
                })
            if err != nil {
                panic(err)
            }
            taskID := taskIDList.Tasks[0]
            fmt.Printf("Provisioning — task: %s\n", taskID)

            // Step 4. Poll task until FINISHED
            var clusterID string
            for {
                task, err := client.Cloud.Tasks.Get(ctx, taskID)
                if err != nil {
                    panic(err)
                }
                if task.State == "FINISHED" {
                    clusterID = task.CreatedResources.AIClusters[0]
                    fmt.Printf("Cluster ID: %s\n", clusterID)
                    break
                }
                if task.State == "ERROR" {
                    panic(fmt.Sprintf("Cluster creation failed: %v", task.Error))
                }
                fmt.Printf("  state=%s — retrying in 10s…\n", task.State)
                time.Sleep(10 * time.Second)
            }

            // Step 5. Get the public IP and print SSH command
            ifaces, err := client.Cloud.GPUVirtual.Clusters.Interfaces.List(ctx,
                clusterID,
                cloud.GPUVirtualClusterInterfaceListParams{})
            if err != nil {
                panic(err)
            }
            for _, iface := range ifaces.Results {
                if iface.Network.External {
                    ip := iface.IPAssignments[0].IPAddress
                    fmt.Printf("SSH: ssh ubuntu@%s\n", ip)
                }
            }
        }
        ```
      </Tab>
    </Tabs>

    ## Step-by-step

    <p>Each step below explains the call, its key parameters, and what the response looks like.</p>

    <Accordion title="Show all steps">
      ### Step 1. List available flavors

      <p>Returns all GPU flavors available in the region. Use the response to pick a flavor name — the name is required when creating the cluster.</p>

      | Field                           | Description                                                               |
      | ------------------------------- | ------------------------------------------------------------------------- |
      | `name`                          | Flavor identifier — pass this value as `flavor` when creating the cluster |
      | `hardware_description.gpu`      | GPU model name and VRAM                                                   |
      | `hardware_properties.gpu_count` | Number of GPUs per node                                                   |
      | `hardware_properties.nic_ib`    | InfiniBand configuration, if present                                      |
      | `capacity`                      | Available node count in the region (`0` means currently out of stock)     |

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          from gcore import Gcore

          client = Gcore()

          flavors = client.cloud.gpu_virtual.clusters.flavors.list()
          for f in flavors.results:
              print(f.name, f.hardware_description.gpu, f"GPUs={f.hardware_properties.gpu_count}")
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          gpuVirtualFlavorList, err := client.Cloud.GPUVirtual.Clusters.Flavors.List(ctx,
              cloud.GPUVirtualClusterFlavorListParams{})
          if err != nil {
              panic(err)
          }
          for _, f := range gpuVirtualFlavorList.Results {
              fmt.Printf("%s  gpu=%s  count=%d\n",
                  f.Name, f.HardwareDescription.GPU, f.HardwareProperties.GPUCount)
          }
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl "https://api.gcore.com/cloud/v3/gpu/virtual/${GCORE_CLOUD_PROJECT_ID}/${GCORE_CLOUD_REGION_ID}/flavors" \
            -H "Authorization: APIKey ${GCORE_API_KEY}"
          ```
        </Tab>
      </Tabs>

      The API returns:

      ```json theme={null}
      {
        "count": 4,
        "results": [
          {
            "name": "g3-ai-24-232-1250-h100-80-1",
            "architecture": "x86_64",
            "disabled": false,
            "capacity": 2,
            "hardware_description": {
              "vcpus": 24,
              "ram": 237568,
              "gpu": "NVIDIA H100-1GPU (80GB)",
              "local_storage": 1250
            },
            "hardware_properties": {
              "gpu_model": "h100",
              "gpu_manufacturer": "nvidia",
              "gpu_count": 1,
              "nic_ib": "1x400"
            }
          }
        ]
      }
      ```

      ### Step 2. List available images

      <p>Returns all GPU cluster images available in the region. Use the response to pick an image ID — required when creating the cluster and when configuring the boot volume.</p>

      | Field  | Description                                                                           |
      | ------ | ------------------------------------------------------------------------------------- |
      | `id`   | Image UUID — pass this as `image_id` inside the boot volume when creating the cluster |
      | `name` | Image name; images with the `eni` suffix are optimized for InfiniBand                 |

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          from gcore import Gcore

          client = Gcore()

          images = client.cloud.gpu_virtual.clusters.images.list()
          for img in images.results:
              print(img.id, img.name)
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          imageList, err := client.Cloud.GPUVirtual.Clusters.Images.List(ctx,
              cloud.GPUVirtualClusterImageListParams{})
          if err != nil {
              panic(err)
          }
          for _, img := range imageList.Results {
              fmt.Printf("%s  %s\n", img.ID, img.Name)
          }
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl "https://api.gcore.com/cloud/v3/gpu/virtual/${GCORE_CLOUD_PROJECT_ID}/${GCORE_CLOUD_REGION_ID}/images" \
            -H "Authorization: APIKey ${GCORE_API_KEY}"
          ```
        </Tab>
      </Tabs>

      The API returns:

      ```json theme={null}
      {
        "count": 4,
        "results": [
          {
            "id": "2a818d08-a9d3-4477-9966-2663d46f6a27",
            "name": "gcloud-ai-gpu-ubuntu-24.04-580.126.20-open-13.0.2-vm-v1.14.27"
          },
          {
            "id": "28752f2a-df21-45b6-a14e-23007daffc44",
            "name": "gcloud-ai-gpu-ubuntu-24.04-570.211.01-open-12.8.1-vm-v1.14.27"
          }
        ]
      }
      ```

      ### Step 3. Create the cluster

      <p>Submits the cluster creation request and returns a task ID for tracking provisioning progress.</p>

      | Parameter                                   | Required | Description                                                                  |
      | ------------------------------------------- | -------- | ---------------------------------------------------------------------------- |
      | `name`                                      | Yes      | Cluster name (two–63 characters, alphanumeric, hyphens, underscores)         |
      | `flavor`                                    | Yes      | Flavor name from Step 1                                                      |
      | `servers_count`                             | Yes      | Number of nodes to provision                                                 |
      | `servers_settings.interfaces`               | Yes      | At least one interface — use `"type": "external"` for public internet access |
      | `servers_settings.volumes[].source`         | Yes      | Must be `"image"` for boot volumes                                           |
      | `servers_settings.volumes[].image_id`       | Yes      | Image UUID from Step 2                                                       |
      | `servers_settings.credentials.ssh_key_name` | Yes      | Name of an existing SSH key in the project                                   |
      | `tags`                                      | No       | Key-value metadata tags                                                      |

      <Info>
        Place `image_id` inside the volume object only; the boot volume `source` must be `"image"`, not `"new"`. Use volume `type: "ssd_hiiops"` — not all volume types are available in every region.
      </Info>

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          import os
          from gcore import Gcore

          client = Gcore()

          task_id_list = client.cloud.gpu_virtual.clusters.create(
              name="my-gpu-cluster",
              flavor="{FLAVOR_NAME}",
              servers_count=1,
              servers_settings={
                  "interfaces": [{"type": "external"}],
                  "volumes": [{
                      "boot_index": 0,
                      "name": "boot-disk",
                      "size": 50,
                      "source": "image",
                      "type": "ssd_hiiops",
                      "image_id": "{IMAGE_ID}",
                  }],
                  "credentials": {"ssh_key_name": os.environ["GCORE_SSH_KEY_NAME"]},
              },
              tags={"env": "production"},
          )
          print(task_id_list.tasks)  # save as TASK_ID
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          taskIDList, err := client.Cloud.GPUVirtual.Clusters.New(ctx,
              cloud.GPUVirtualClusterNewParams{
                  Name:         "my-gpu-cluster",
                  Flavor:       "{FLAVOR_NAME}",
                  ServersCount: 1,
                  ServersSettings: cloud.GPUVirtualClusterNewParamsServersSettings{
                      Interfaces: []cloud.GPUVirtualClusterNewParamsServersSettingsInterfaceUnion{{
                          OfExternal: &cloud.GPUVirtualClusterNewParamsServersSettingsInterfaceExternal{},
                      }},
                      Volumes: []cloud.GPUVirtualClusterNewParamsServersSettingsVolumeUnion{{
                          OfImage: &cloud.GPUVirtualClusterNewParamsServersSettingsVolumeImage{
                              BootIndex: int64(0),
                              Name:      "boot-disk",
                              Size:      int64(50),
                              Type:      "ssd_hiiops",
                              ImageID:   "{IMAGE_ID}",
                          },
                      }},
                      Credentials: cloud.GPUVirtualClusterNewParamsServersSettingsCredentials{
                          SSHKeyName: gcore.String(os.Getenv("GCORE_SSH_KEY_NAME")),
                      },
                  },
              })
          if err != nil {
              panic(err)
          }
          fmt.Printf("%+v\n", taskIDList.Tasks)  // save as TASK_ID
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl -X POST \
            "https://api.gcore.com/cloud/v3/gpu/virtual/${GCORE_CLOUD_PROJECT_ID}/${GCORE_CLOUD_REGION_ID}/clusters" \
            -H "Authorization: APIKey ${GCORE_API_KEY}" \
            -H "Content-Type: application/json" \
            -d '{
              "name": "my-gpu-cluster",
              "flavor": "{FLAVOR_NAME}",
              "image_id": "{IMAGE_ID}",
              "servers_count": 1,
              "servers_settings": {
                "interfaces": [{"type": "external"}],
                "volumes": [{
                  "boot_index": 0,
                  "name": "boot-disk",
                  "size": 50,
                  "source": "image",
                  "type": "ssd_hiiops",
                  "image_id": "{IMAGE_ID}"
                }],
                "credentials": {"ssh_key_name": "{YOUR_SSH_KEY_NAME}"}
              },
              "tags": {"env": "production"}
            }'
          ```
        </Tab>
      </Tabs>

      The API returns:

      ```json theme={null}
      { "tasks": ["b1c2de3f-7e4a-49b7-8603-ebf743df8c9f"] }
      ```

      ### Step 4. Wait for provisioning

      <p>Poll the task endpoint every 10 seconds until `state` is `FINISHED`. Virtual GPU nodes typically provision in 3–8 minutes.</p>

      ```bash theme={null}
      curl "https://api.gcore.com/cloud/v1/tasks/b1c2de3f-7e4a-49b7-8603-ebf743df8c9f" \
        -H "Authorization: APIKey ${GCORE_API_KEY}"
      ```

      While provisioning:

      ```json theme={null}
      {
        "id": "b1c2de3f-7e4a-49b7-8603-ebf743df8c9f",
        "state": "RUNNING",
        "created_resources": null
      }
      ```

      When complete:

      ```json theme={null}
      {
        "id": "b1c2de3f-7e4a-49b7-8603-ebf743df8c9f",
        "state": "FINISHED",
        "created_resources": {
          "ai_clusters": ["ca685f02-2c56-4dfd-bf27-f105ad38dc6a"]
        }
      }
      ```

      <p>Read the cluster ID from `created_resources.ai_clusters[0]`.</p>

      ### Step 5. Get the cluster public IP

      <p>Retrieve the network interfaces to find the node's public IP address for SSH access.</p>

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          ifaces = client.cloud.gpu_virtual.clusters.interfaces.list(cluster_id="{CLUSTER_ID}")
          for iface in ifaces.results:
              if iface.network.external:
                  print("Public IP:", iface.ip_assignments[0].ip_address)
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          ifaces, err := client.Cloud.GPUVirtual.Clusters.Interfaces.List(ctx,
              "{CLUSTER_ID}",
              cloud.GPUVirtualClusterInterfaceListParams{})
          if err != nil {
              panic(err)
          }
          for _, iface := range ifaces.Results {
              if iface.Network.External {
                  fmt.Println("Public IP:", iface.IPAssignments[0].IPAddress)
              }
          }
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl "https://api.gcore.com/cloud/v3/gpu/virtual/${GCORE_CLOUD_PROJECT_ID}/${GCORE_CLOUD_REGION_ID}/clusters/{CLUSTER_ID}/interfaces" \
            -H "Authorization: APIKey ${GCORE_API_KEY}"
          ```
        </Tab>
      </Tabs>

      The API returns:

      ```json theme={null}
      {
        "count": 2,
        "results": [
          {
            "port_id": "3b5835e7-33dc-4168-87c3-166601959ebd",
            "ip_assignments": [{ "ip_address": "85.234.66.69" }],
            "network": { "name": "pub_net", "external": true }
          },
          {
            "port_id": "0882597d-67f5-4cf5-8168-cbb20f47ad35",
            "ip_assignments": [{ "ip_address": "192.168.242.176" }],
            "network": { "name": "gpu-cluster-ib-network-ca685f02-…", "external": false }
          }
        ]
      }
      ```

      <p>The interface with `"external": true` is the public network. Use its `ip_assignments[0].ip_address` for SSH.</p>
    </Accordion>

    ## Connect to the cluster

    <p>After the cluster reaches `active` status, connect to a node via SSH using the `ubuntu` user:</p>

    ```bash theme={null}
    ssh ubuntu@{NODE_PUBLIC_IP}
    ```

    <p>Verify that GPUs are detected:</p>

    ```bash theme={null}
    nvidia-smi
    ```

    <p>A successful output shows the list of GPUs, driver version, and CUDA version. For nodes with only private interfaces, connect through a bastion host or VPN.</p>
  </MethodSection>

  <MethodSection id="terraform" label="Terraform">
    <p>Declare a Virtual GPU cluster as code using the [`gcore_cloud_gpu_virtual_cluster`](https://registry.terraform.io/providers/G-Core/gcore/latest/docs/resources/cloud_gpu_virtual_cluster) resource from the [Terraform provider](/developer-tools/terraform/overview) v2.</p>

    ## Create a Virtual GPU cluster

    <p>Provisions one node with a public interface and an `ssd_hiiops` boot volume. Use the flavors and images endpoints from the REST API tab to identify the right `flavor` and `image_id` for the target region before applying.</p>

    ```hcl theme={null}
    resource "gcore_cloud_gpu_virtual_cluster" "example" {
      name          = "my-gpu-cluster"
      flavor        = "g3-ai-24-232-1250-h100-80-1"  # 1× H100 80 GB; list available flavors via the flavors API
      servers_count = 1
      project_id    = var.project_id
      region_id     = var.region_id

      # servers_settings is an attribute (nested type), not a block — use = assignment
      servers_settings = {
        interfaces = [
          { type = "external" }
        ]
        volumes = [
          {
            boot_index = 0
            name       = "boot-disk"
            size       = 50     # minimum 50 GiB; increase as needed
            source     = "image"
            type       = "ssd_hiiops"
            image_id   = "2a818d08-a9d3-4477-9966-2663d46f6a27"  # Ubuntu 24.04, CUDA 13.0, driver 580 — Sines-3; list current images via the images API
          }
        ]
        credentials = {
          ssh_key_name = "my-ssh-key"  # name of an existing SSH key in the project
        }
      }
    }

    output "cluster_id" {
      value = gcore_cloud_gpu_virtual_cluster.example.id
    }

    output "cluster_status" {
      value = gcore_cloud_gpu_virtual_cluster.example.status
    }
    ```

    <p>After `terraform apply` completes, the cluster status is `active`. Connect via SSH using the public IP from the cluster's network interfaces.</p>

    ## Delete the cluster

    <p>Remove the resource block — Terraform detects the missing declaration and deletes the cluster on the next `terraform apply`.</p>

    ```hcl theme={null}
    # Remove or comment out this block:
    # resource "gcore_cloud_gpu_virtual_cluster" "example" {
    #   name          = "my-gpu-cluster"
    #   ...
    # }
    ```

    ```bash theme={null}
    terraform apply
    ```
  </MethodSection>
</MethodSwitch>
