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

# Create a Bare Metal 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>Bare Metal GPU clusters provision one or more dedicated GPU servers with no virtualization layer. They are intended for AI training, inference, distributed workloads, and other applications that require full hardware performance. Two cluster types are available: **Bare Metal GPU** for on-demand dedicated servers, and **[Spot GPU](/edge-ai/ai-infrastructure/spot-bare-metal-gpu)** for discounted interruptible capacity that may be reclaimed with 24 hours' notice. Available GPU models and cluster types vary by region.</p>

    ## Cluster architecture

    <p>A cluster consists of one or more bare metal GPU servers provisioned from an identical template. All nodes share the same image, flavor, and network configuration.</p>

    ```
    Cluster
     ├─ Node 1  (GPU server)
     ├─ Node 2  (GPU server)
     ├─ ...
     ├─ InfiniBand fabric  (auto-configured when the flavor supports it)
     └─ File Share         (optional — shared storage across all nodes)
    ```

    <p>InfiniBand inter-node networking is configured automatically when the selected flavor supports it, enabling efficient distributed training without manual network setup. File shares mount across all nodes and persist independently of cluster state.</p>

    ## Create a GPU cluster

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

    <Steps>
      <Step title="Select region">
        In the **Region** section, select the data center location for the cluster. Regions are grouped by geography (Asia-Pacific, EMEA). Each region card shows its availability status — file share integration, for instance, is available only in select regions.

        <Frame>
          <img src="https://mintcdn.com/gcore-docs/1EEmhbY3hLFWRm36/images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-region.png?fit=max&auto=format&n=1EEmhbY3hLFWRm36&q=85&s=a44844f232ac2bfb0f162f7764d1678f" alt="Region selection section showing available regions grouped by geography" width="955" height="415" data-path="images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-region.png" />
        </Frame>

        <Info>
          GPU model availability and pricing vary by region. Check other regions if a required GPU model is not available. For help with availability, contact the [sales team](https://gcore.com/contact-sales).
        </Info>
      </Step>

      <Step title="Configure cluster capacity">
        In the **Cluster capacity** section, select the GPU model and flavor that define the hardware configuration for every node. Available options depend on the selected region.

        * **GPU Cluster type**: Select **Bare Metal GPU** for dedicated servers, or **Spot Bare Metal GPU** for discounted interruptible capacity — pricing and reclamation details are on the [Spot GPU](/edge-ai/ai-infrastructure/spot-bare-metal-gpu) page.
        * **GPU Model**: A100, H100, or H200 — availability depends on the region.
        * **Show out of stock**: Toggle to show or hide unavailable flavors.
        * **Flavor**: Each card displays GPU configuration, CPU type, RAM, storage, network connectivity, and pricing.

        <Frame>
          <img src="https://mintcdn.com/gcore-docs/1EEmhbY3hLFWRm36/images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-capacity.png?fit=max&auto=format&n=1EEmhbY3hLFWRm36&q=85&s=167165ac5b0618935c40485bae5acc62" alt="Cluster capacity section showing GPU Cluster type, GPU Model selector, and flavor card with specifications" width="955" height="545" data-path="images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-capacity.png" />
        </Frame>
      </Step>

      <Step title="Set the number of instances">
        In the **Number of Instances** section, specify how many servers to provision. Each instance is a separate physical server with the selected flavor configuration. The maximum depends on regional stock availability — there is no fixed per-cluster limit.

        <Frame>
          <img src="https://mintcdn.com/gcore-docs/1EEmhbY3hLFWRm36/images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-instances.png?fit=max&auto=format&n=1EEmhbY3hLFWRm36&q=85&s=fd1764fa39e599e919c66e5ad60084e6" alt="Number of Instances section with instance counter" width="955" height="187" data-path="images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-instances.png" />
        </Frame>

        <Info>
          After creation, the cluster can be resized. Scaling up adds nodes with the same configuration used at creation. Scaling down removes a random node — to delete a specific node, use the per-node delete action in the cluster details. Deleting the last node deletes the entire cluster.
        </Info>
      </Step>

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

        In the **Image** section, select the operating system:

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

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

        <Frame>
          <img src="https://mintcdn.com/gcore-docs/1EEmhbY3hLFWRm36/images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-image.png?fit=max&auto=format&n=1EEmhbY3hLFWRm36&q=85&s=0e66b11c65c47a3198629124b5984b7c" alt="Image section with Public and Custom tabs and image selector" width="955" height="284" data-path="images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-image.png" />
        </Frame>
      </Step>

      <Step title="Configure file share integration (optional)">
        File shares provide shared storage accessible from all cluster nodes. GPU-enabled regions use VAST-backed high-performance shares. This option appears only in regions where file shares are available — — [file shares](/cloud/file-shares/configure-file-shares) documentation describes share types, sizing, and access rules.

        1. Enable the **File Share integration** checkbox.
        2. Select an existing file share or create a new one.
        3. Specify the mount path on cluster nodes (default: `/home/ubuntu/mnt/nfs`).

        <Frame>
          <img src="https://mintcdn.com/gcore-docs/1EEmhbY3hLFWRm36/images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-file-share.png?fit=max&auto=format&n=1EEmhbY3hLFWRm36&q=85&s=35c7c1218260a2773ff1f0ef5b245be9" alt="File share integration section with Enable File Share checkbox" width="955" height="167" data-path="images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-file-share.png" />
        </Frame>

        <Info>
          When file share integration is enabled, the cluster automatically adds mount commands to the user data script. If **User data** is also configured in Additional options, do not remove these commands — they handle automatic mounting on first boot.
        </Info>
      </Step>

      <Step title="Configure network settings">
        Network settings define how the cluster communicates with external services and other resources. At least one interface is required.

        In the **Network settings** section, select the interface type:

        | 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**. All public interfaces include Basic DDoS Protection at no additional cost.

        Each interface can be expanded to configure [security groups](/cloud/networking/add-and-configure-a-firewall). The `default` security group is pre-selected — if no security group is selected, it is attached automatically.

        <Frame>
          <img src="https://mintcdn.com/gcore-docs/0NGyxV-WwpoVDck7/images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/network-settings-security-groups.png?fit=max&auto=format&n=0NGyxV-WwpoVDck7&q=85&s=4cd4240ebbc3103bf46bc9640927f5dc" alt="Security Groups field with default group selected per interface" width="844" height="687" data-path="images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-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-docs/1EEmhbY3hLFWRm36/images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-ssh-key.png?fit=max&auto=format&n=1EEmhbY3hLFWRm36&q=85&s=a4a121a2271a5c759c49220119c8bd23" alt="SSH key section with dropdown and options to add or generate keys" width="955" height="200" data-path="images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/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-docs/1EEmhbY3hLFWRm36/images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-additional-options.png?fit=max&auto=format&n=1EEmhbY3hLFWRm36&q=85&s=fd92fdb8aa8b3641395cfcb27501665a" alt="Additional options section with User data and Add tags checkboxes" width="955" height="188" data-path="images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/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**.

        <Frame>
          <img src="https://mintcdn.com/gcore-docs/1EEmhbY3hLFWRm36/images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-name.png?fit=max&auto=format&n=1EEmhbY3hLFWRm36&q=85&s=cfaca3445d2245a4d167dac0a7aa112f" alt="GPU Cluster Name section with name input field" width="955" height="166" data-path="images/docs/edge-ai/ai-infrastructure/create-a-bare-metal-gpu-cluster/gpu-cluster-name.png" />
        </Frame>

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

        <Warning>
          Cluster-level settings (image, file share integration, 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 and verify 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@<instance-ip-address>
        ```

        Replace `<instance-ip-address>` with the public or floating IP shown in the cluster details. 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).
      </Step>

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

        A successful output shows all available GPUs, driver version, and CUDA version. If no GPUs appear, check that the image includes the correct NVIDIA drivers for the GPU model.

        If file share integration was enabled, verify the mount is accessible:

        ```bash theme={null}
        ls /home/ubuntu/mnt/nfs
        ```
      </Step>
    </Steps>

    <p>Clusters can also be created programmatically using the [GPU API](/api-reference/cloud/gpu-bare-metal).</p>
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>Create a Bare Metal 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).
      Bare Metal GPU clusters are available in select regions — the [List flavors](#step-1-list-available-flavors) call confirms availability for the target region.
    </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_baremetal.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_baremetal.clusters.images.list()
        image = images.results[0]
        print(f"Image: {image.name}")

        # Step 3. Create the cluster
        task_id_list = client.cloud.gpu_baremetal.clusters.create(
            name="my-gpu-cluster",
            flavor=flavor.name,
            image_id=image.id,
            servers_count=1,
            servers_settings={
                "interfaces": [{"type": "external"}],
                "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.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 15s…")
            time.sleep(15)

        # Step 5. Get the public IP and print SSH command
        ifaces = client.cloud.gpu_baremetal.clusters.interfaces.list(
            cluster_id=cluster_id,
        )
        for iface in ifaces.results:
            if iface.network_details.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.GPUBaremetal.Clusters.Flavors.List(ctx,
                cloud.GPUBaremetalClusterFlavorListParams{})
            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.GPUBaremetal.Clusters.Images.List(ctx,
                cloud.GPUBaremetalClusterImageListParams{})
            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.GPUBaremetal.Clusters.New(ctx,
                cloud.GPUBaremetalClusterNewParams{
                    Name:         "my-gpu-cluster",
                    Flavor:       flavor.Name,
                    ImageID:      image.ID,
                    ServersCount: 1,
                    ServersSettings: cloud.GPUBaremetalClusterNewParamsServersSettings{
                        Interfaces: []cloud.GPUBaremetalClusterNewParamsServersSettingsInterfaceUnion{{
                            OfExternal: &cloud.GPUBaremetalClusterNewParamsServersSettingsInterfaceExternal{},
                        }},
                        Credentials: cloud.GPUBaremetalClusterNewParamsServersSettingsCredentials{
                            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.Clusters[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 15s…\n", task.State)
                time.Sleep(15 * time.Second)
            }

            // Step 5. Get the public IP and print SSH command
            ifaces, err := client.Cloud.GPUBaremetal.Clusters.Interfaces.List(ctx,
                clusterID,
                cloud.GPUBaremetalClusterInterfaceListParams{})
            if err != nil {
                panic(err)
            }
            for _, iface := range ifaces.Results {
                if iface.NetworkDetails.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 Bare Metal GPU flavors available in the region. Use the response to pick a flavor name — 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_baremetal.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}
          flavorList, err := client.Cloud.GPUBaremetal.Clusters.Flavors.List(ctx,
              cloud.GPUBaremetalClusterFlavorListParams{})
          if err != nil {
              panic(err)
          }
          for _, f := range flavorList.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/baremetal/${GCORE_CLOUD_PROJECT_ID}/${GCORE_CLOUD_REGION_ID}/flavors" \
            -H "Authorization: APIKey ${GCORE_API_KEY}"
          ```
        </Tab>
      </Tabs>

      The API returns:

      ```json theme={null}
      {
        "count": 2,
        "results": [
          {
            "name": "bm3-ai-1xlarge-h100-80-8",
            "architecture": "x86_64",
            "disabled": false,
            "capacity": 0,
            "hardware_description": {
              "cpu": "2x Intel Xeon 8480+",
              "ram": "2TB RAM",
              "disk": "8 x 3.84 TB NVMe",
              "network": "3.2 Tbit/s Infiniband",
              "gpu": "NVIDIA H100-8GPU (80GB)"
            },
            "hardware_properties": {
              "gpu_model": "h100",
              "gpu_manufacturer": "nvidia",
              "gpu_count": 8,
              "nic_eth": "2x100",
              "nic_ib": "8x400"
            }
          }
        ]
      }
      ```

      ### Step 2. List available images

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

      | Field  | Description                                                           |
      | ------ | --------------------------------------------------------------------- |
      | `id`   | Image UUID — pass this as `image_id` 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_baremetal.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.GPUBaremetal.Clusters.Images.List(ctx,
              cloud.GPUBaremetalClusterImageListParams{})
          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/baremetal/${GCORE_CLOUD_PROJECT_ID}/${GCORE_CLOUD_REGION_ID}/images" \
            -H "Authorization: APIKey ${GCORE_API_KEY}"
          ```
        </Tab>
      </Tabs>

      The API returns:

      ```json theme={null}
      {
        "count": 9,
        "results": [
          {
            "id": "e975eac6-e81e-49b6-a0ca-967786907961",
            "name": "gcloud-ai-gpu-ubuntu-24.04-580.126.16-open-13.0.2-bm-v1.14.20"
          },
          {
            "id": "0e2600fe-df82-40bf-97f1-0b4b1f8bc9e0",
            "name": "ubuntu-22.04-x64-nvidia-a100/h100-eni"
          }
        ]
      }
      ```

      ### 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 chars, alphanumeric, hyphens, underscores)              |
      | `flavor`                                    | Yes      | Flavor name from Step 1                                                      |
      | `image_id`                                  | Yes      | Image UUID from Step 2                                                       |
      | `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.credentials.ssh_key_name` | Yes      | Name of an existing SSH key in the project                                   |
      | `tags`                                      | No       | Key-value metadata tags                                                      |

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

          client = Gcore()

          task_id_list = client.cloud.gpu_baremetal.clusters.create(
              name="my-gpu-cluster",
              flavor="{FLAVOR_NAME}",
              image_id="{IMAGE_ID}",
              servers_count=1,
              servers_settings={
                  "interfaces": [{"type": "external"}],
                  "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.GPUBaremetal.Clusters.New(ctx,
              cloud.GPUBaremetalClusterNewParams{
                  Name:         "my-gpu-cluster",
                  Flavor:       "{FLAVOR_NAME}",
                  ImageID:      "{IMAGE_ID}",
                  ServersCount: 1,
                  ServersSettings: cloud.GPUBaremetalClusterNewParamsServersSettings{
                      Interfaces: []cloud.GPUBaremetalClusterNewParamsServersSettingsInterfaceUnion{{
                          OfExternal: &cloud.GPUBaremetalClusterNewParamsServersSettingsInterfaceExternal{},
                      }},
                      Credentials: cloud.GPUBaremetalClusterNewParamsServersSettingsCredentials{
                          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/baremetal/${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"}],
                "credentials": {"ssh_key_name": "{YOUR_SSH_KEY_NAME}"}
              },
              "tags": {"env": "production"}
            }'
          ```
        </Tab>
      </Tabs>

      The API returns:

      ```json theme={null}
      { "tasks": ["e4cecbfc-5258-40bb-b78f-3cd92621c3b2"] }
      ```

      ### Step 4. Wait for provisioning

      <p>Poll the task endpoint every 15 seconds until `state` is `FINISHED`. Bare Metal GPU nodes typically provision in 15–20 minutes.</p>

      ```bash theme={null}
      curl "https://api.gcore.com/cloud/v1/tasks/e4cecbfc-5258-40bb-b78f-3cd92621c3b2" \
        -H "Authorization: APIKey ${GCORE_API_KEY}"
      ```

      <p>While provisioning:</p>

      ```json theme={null}
      {
        "id": "e4cecbfc-5258-40bb-b78f-3cd92621c3b2",
        "state": "RUNNING",
        "created_resources": null
      }
      ```

      <p>When complete:</p>

      ```json theme={null}
      {
        "id": "e4cecbfc-5258-40bb-b78f-3cd92621c3b2",
        "state": "FINISHED",
        "created_resources": {
          "clusters": ["4146c059-501a-409f-8fe1-04de091d917a"]
        }
      }
      ```

      <p>Read the cluster ID from `created_resources.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_baremetal.clusters.interfaces.list(cluster_id="{CLUSTER_ID}")
          for iface in ifaces.results:
              if iface.network_details.external:
                  print("Public IP:", iface.ip_assignments[0].ip_address)
          ```
        </Tab>

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

        <Tab title="curl">
          ```bash theme={null}
          curl "https://api.gcore.com/cloud/v3/gpu/baremetal/${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": 1,
        "results": [
          {
            "port_id": "302f6e9c-ff95-4d67-ac19-2d3473e2c3ee",
            "ip_assignments": [{ "ip_address": "85.234.84.39", "subnet_id": "351b0dd7-ca09-431c-be53-935db3785067" }],
            "network_details": { "name": "pub_net", "external": true }
          }
        ]
      }
      ```

      <p>The interface with `network_details.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 Bare Metal GPU cluster as code using the [`gcore_cloud_gpu_baremetal_cluster`](https://registry.terraform.io/providers/G-Core/gcore/latest/docs/resources/cloud_gpu_baremetal_cluster) resource from the [Terraform provider](/developer-tools/terraform/overview) v2.</p>

    ## Create a Bare Metal GPU cluster

    <p>Provisions one bare metal node with a public interface. 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_baremetal_cluster" "example" {
      project_id    = var.project_id
      region_id     = var.region_id
      name          = "my-bm-gpu-cluster"
      flavor        = "bm3-ai-1xlarge-a100-80-8"  # 8× A100 80 GB; list available flavors via the flavors API
      image_id      = "d9400449-0ee8-4c60-adb9-fc545a80325c"  # Ubuntu 22.04, driver 580, CUDA 13.0 — Luxembourg-2; list current images via the images API
      servers_count = 1

      # servers_settings is an attribute (nested type), not a block — use = assignment
      servers_settings = {
        interfaces = [
          { type = "external" }
        ]
        credentials = {
          ssh_key_name = "my-ssh-key"  # name of an existing SSH key in the project
        }
      }
    }

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

    output "cluster_status" {
      value = gcore_cloud_gpu_baremetal_cluster.example.status
    }

    # terraform import gcore_cloud_gpu_baremetal_cluster.example '<project_id>/<region_id>/<cluster_id>'
    ```

    <p>Bare Metal nodes take 15–20 minutes to provision. After `terraform apply` completes, the cluster status is `active`. Connect via SSH to the public IP from the cluster's network interfaces.</p>

    <Info>
      `ssh_key_name` must be a literal string in the HCL file — a variable reference causes the provider to reject the configuration at plan time. Replace `"my-ssh-key"` with the name of an existing SSH key in the project.
    </Info>

    ## 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_baremetal_cluster" "example" {
    #   name          = "my-bm-gpu-cluster"
    #   ...
    # }
    ```

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