> ## 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 instance

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>Create Linux or Windows Virtual Instances in Gcore Cloud.</p>

    <Tip>
      When using [VAST file shares](/cloud/file-shares/configure-file-shares), it is best to create them **before** provisioning the corresponding compute resources.
    </Tip>

    ## Step 1. Select a region

    1. In the [Gcore Customer Portal](https://portal.gcore.com/accounts/reports/dashboard), go to **Virtual Instances** and click **Create Instance**.

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/1-create-instancebutton.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=ed4ca7c779ef779844bd471d9b2f7005" alt="The Create VM button" width="1440" height="463" data-path="images/docs/cloud/virtual-instances/create-an-instance/1-create-instancebutton.png" />
    </Frame>

    2. Choose the region where the Virtual Instance will be deployed.

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/2-region-selection.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=d9ab92f3325dfa4d0d79cd28305e254c" alt="Region selection" width="896" height="457" data-path="images/docs/cloud/virtual-instances/create-an-instance/2-region-selection.png" />
    </Frame>

    <p>Notice that regions are labeled either Core or Edge. This represents the region's equipment specifications.</p>

    |                                          | Core                        | Edge\*                          |
    | ---------------------------------------- | --------------------------- | ------------------------------- |
    | Equipment generation                     | The latest                  | Different                       |
    | Designed for high scalability on the fly | Yes                         | Not                             |
    | Available resources                      | 1000 cores and 30 TB of RAM | Up to 300 cores and 1 TB of RAM |
    | Ports for user traffic and storage       | Separate                    | Shared                          |
    | Price                                    | Higher                      | Lower                           |

    * Edge regions can be transformed into Core regions — contact [support](mailto:support@gcore.com) to request the conversion.

    ## Step 2. Configure the image

    <p>Select the type of hardware architecture for the instance:</p>

    * **x86-64** : This architecture is known for its broad compatibility with Linux operating systems and Windows distributions. It is commonly used in general purpose computing applications.
    * **ARM** : ARM architecture is designed for energy efficiency and low power consumption, which also supports strong performance, making it ideal for high-performance computing tasks. However, ARM Virtual Machines are compatible with fewer OS distributions.

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/configure-image.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=a6773fce0af2618b35a806a680e3a7d8" alt="Choose x86-64 or ARM architecture" width="981" height="757" data-path="images/docs/cloud/virtual-instances/create-an-instance/configure-image.png" />
    </Frame>

    <p>The selected hardware architecture determines the available OS options and instance flavors. Select the image source from one of the tabs:</p>

    * **Distributions** — standard OS distributions such as Ubuntu, Debian, CentOS, Windows, and others.
    * **Marketplace** — pre-configured templates with software already installed.
    * **Volume** — boot from an existing volume.
    * **Snapshot** — boot from a previously created snapshot.
    * **Custom Images** — use an image uploaded to the project.

    ## Step 3. Choose the VM type

    <p>The available CPU generation is determined by the selected region and displayed in the **CPU generation** field. Select one of the available flavors:</p>

    * **Standard** — balanced compute, memory, and networking for a wide range of workloads.
    * **CPU-Optimized** — higher vCPU-to-RAM ratio, suited for CPU-intensive tasks — batch processing, video encoding, and large-scale data processing.
    * **Memory-Optimized** — higher RAM-to-vCPU ratio, suited for memory-intensive workloads — databases, SRM/ERP, and data warehouses.

    <Info>
      **Info**

      GPU, High-frequency, and Shared flavors are available in select regions only. The flavor tabs appear automatically when those flavors are available in the selected region.
    </Info>

    ## Step 4. Set up volumes

    <p>Enter a volume name, choose its type, and set its size in GiB.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/add-volume.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=abdfc69118512203133f5b1dc489b5de" alt="Configure Volumes" width="1440" height="900" data-path="images/docs/cloud/virtual-instances/create-an-instance/add-volume.png" />
    </Frame>

    <Accordion title="Available volume types">
      * **High IOPS SSD**. High-performance SSD block storage designed for latency-sensitive transactional workloads (60 IOPS per 1 GB; 2.5 MB/s per 1 GiB). The IOPS performance limit is 9,000. The bandwidth limit is 500 MB/s.

      * **Standard**. Network SSD disk that provides stable and high random I/O performance, as well as high data reliability (6 IOPS per 1 GB; 0.4 MB/s per 1 GiB). The IOPS performance limit is 4,500. The bandwidth limit is 300 MB/s.

      * **SSD Low-Latency**. SSD block storage designed for applications that require low-latency storage and real-time data processing. It can achieve IOPS performance of up to 5,000, with an average latency of 300 µs.
    </Accordion>

    <p>(Optional) Add an **Attachment Tag**.</p>

    ## Step 5. Configure networking

    ### Configure the interface

    <p>Configure the network interface:</p>

    1. Select the **Network type**: **Public** or **Private**.

    2. In the **IP Family** section, select the IP version: **IPv4**, **IPv6**, or **Dual (IPv4 + IPv6)**.

    3. In the **Security Groups** section, select the security groups to attach. The default security group is attached automatically if no selection is made.

    4. (Optional) Enable **Use reserved IP** to assign a [reserved IP address](/cloud/networking/ip-address/create-and-configure-a-reserved-ip-address) to the instance, or enable **Use floating IP** to assign a [floating IP address](/cloud/networking/ip-address/create-and-configure-a-floating-ip-address).

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/vm-network-section.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=e552ab119a1983fc2d4552b03cf2b81e" alt="Network settings section with Public network interface selected" width="655" height="510" data-path="images/docs/cloud/virtual-instances/create-an-instance/vm-network-section.png" />
    </Frame>

    <Info>
      **Info**

      The default security group allows inbound traffic over ICMP, SSH (TCP/22), and RDP (TCP/3389 and UDP/3389). Outbound traffic is unrestricted except SMTP (TCP/25 and UDP/25), which is blocked by default. [Custom security groups](/cloud/networking/add-and-configure-a-firewall) can override these defaults.
    </Info>

    <p>For a **private** interface, configure a network and a subnetwork as described below.</p>

    <Tip>
      If both a public and private interface are needed, disable the default gateway on the private network's subnetwork and assign a floating IP to the private interface.
    </Tip>

    ### Configure the network

    <p>Select an existing network from the dropdown list or create a new one by clicking **Add a new network**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/add-new-network.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=a2cf8d9f45e182ac6a42ae46cf4edee8" alt="Private interface with highlighted Add a new network link" width="656" height="512" data-path="images/docs/cloud/virtual-instances/create-an-instance/add-new-network.png" />
    </Frame>

    <Accordion title="Create a new network">
      1. Click **Add a new network**.
      2. Enter the network name.
      3. (Optional) Enable **Bare Metal Network** to make the network available to Bare Metal servers.
      4. (Optional) Enable **Create router** to create a router that all subnets in this network will connect to by default.
      5. (Optional) Enable **Add tags** to attach metadata to the network.
      6. Click **Create & Proceed**.

      <Frame>
        <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/create-network.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=a600fc26e2dee4a64c29f190d2a260ac" alt="Add a new network" width="1440" height="900" data-path="images/docs/cloud/virtual-instances/create-an-instance/create-network.png" />
      </Frame>
    </Accordion>

    ### Configure the subnetwork

    <Info>
      **Info**

      When a Virtual Instance has several subnetworks, [verify that only one subnetwork is routable](/cloud/networking/create-and-manage-a-subnetwork#set-up-the-default-gateway) to avoid a default gateway conflict that would prevent connecting to the instance.
    </Info>

    <p>Select an existing subnetwork from the dropdown list or create a new one by clicking **Add a new subnetwork**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/add-new-subnetwork.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=de89509114c18b8dd1174c065e488440" alt="Add a new subnetwork" width="670" height="749" data-path="images/docs/cloud/virtual-instances/create-an-instance/add-new-subnetwork.png" />
    </Frame>

    <Accordion title="Create a new subnetwork">
      1. Click **Add a new subnetwork**.
      2. Enter the subnetwork name.
      3. Set CIDR in one of the private ranges: 10.0.0.0–10.255.255.255, 172.16.0.0–172.31.255.255, or 192.168.0.0–192.168.255.255. Set the mask between /16 and /29.
      4. (Optional) Enable **Enable DHCP** to assign IP addresses to machines in the subnet automatically.
      5. (Optional) Enable **Connect to network router** to connect this subnet to the network's router by default.
      6. Select **Gateway IP** assignment: **Automatically** or **Specific**.
      7. (Optional) Enter **Custom DNS** server addresses.
      8. (Optional) Enter static routes using the **Destination** and **Nexthop** fields.
      9. (Optional) Enable **Add tags** to attach metadata to the subnetwork.
      10. Click **Create subnet**.

      <Frame>
        <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/create-subnetwork-annotated.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=b6ee481be42c27b3816d166a009ea1ca" alt="Add a new subnetwork dialog" width="1440" height="900" data-path="images/docs/cloud/virtual-instances/create-an-instance/create-subnetwork-annotated.png" />
      </Frame>
    </Accordion>

    ### Interface order

    <Accordion title="Interface naming when using multiple interfaces">
      <p>When adding multiple interfaces, the order in the creation form determines how the OS names them after the instance starts. The diagram below shows how the selection order carries through to the **Networking** tab and then to the OS interface names:</p>

      <Frame>
        <img src="https://mintcdn.com/gcore/YEsPf7l6EvNuxMEL/images/docs/cloud/virtual-instances/create-an-instance/vm-network-interface-order-diagram.png?fit=max&auto=format&n=YEsPf7l6EvNuxMEL&q=85&s=93e51e761273f8a129ad68ddb507ccc0" alt="Diagram showing how interface order set during VM creation maps to the Networking tab and OS interface names" width="2725" height="639" data-path="images/docs/cloud/virtual-instances/create-an-instance/vm-network-interface-order-diagram.png" />
      </Frame>

      ```bash theme={null}
      $ ip addr show enp3s0
      2: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
          inet 85.234.84.186/24 metric 1000 brd 85.234.84.255 scope global dynamic enp3s0

      $ ip addr show enp4s0
      3: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
          inet 10.0.0.132/24 metric 2000 brd 10.0.0.255 scope global dynamic enp4s0
      ```

      <p>Interface 1 (`enp3s0`) is the public interface; Interface 2 (`enp4s0`) is the private interface. The order set at creation determines routing priority: the first interface has higher priority for outbound traffic than subsequent ones. The interface order cannot be changed after the instance is created.</p>
    </Accordion>

    ## Step 6. Set up authentication

    <Tabs>
      <Tab title="Linux Virtual Machine">
        Configure an SSH key for a remote SSH connection. Add an existing SSH key or generate a new one using the [SSH key guide](/cloud/virtual-instances/connect/connect-via-ssh).

        <Frame>
          <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/11-ssh-key.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=92747112461f67e2c2b68bf4a20a4026" alt="SSH keys section with three options: select, generate, or add a new SSH key" width="677" height="197" data-path="images/docs/cloud/virtual-instances/create-an-instance/11-ssh-key.png" />
        </Frame>

        In addition to SSH keys, a password can also be set for the Virtual Instance, as described in step 7. A password is required to connect to a Linux instance [from the Customer Portal](/cloud/virtual-instances/connect/connect-to-your-instance-via-control-panel#connect-to-a-linux-instance).
      </Tab>

      <Tab title="Windows Virtual Machine">
        Configure **Access** by setting a password for the Admin user.

        <Frame>
          <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/12-access-for-windows-instance.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=ac79b216bb9cc9b17368ca7f6ea01b51" alt="Access field with a password" width="677" height="157" data-path="images/docs/cloud/virtual-instances/create-an-instance/12-access-for-windows-instance.png" />
        </Frame>

        <Info>
          **Info**

          The password must contain between 8 and 16 characters, including at least one lowercase letter (`a-z`), one uppercase letter (`A-Z`), one number (`0-9`), and one special character (`!#$%&'()*+,-./:;<=>?@[]^_{|}~`).
        </Info>

        The password can be used to connect to a Windows instance from the [Customer Portal](/cloud/virtual-instances/connect/connect-to-your-instance-via-control-panel) or from a local machine using RDP (Remote Desktop Protocol).
      </Tab>
    </Tabs>

    ## Step 7 (Optional). Configure additional options

    ### Instance initialization

    <p>Enable the **User data** toggle to customize the instance during the initial boot using a `cloud-init` agent.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/13-add-user-data.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=99484e0b0373e35c055421853645240d" alt="User data field with password configuration" width="1024" height="718" data-path="images/docs/cloud/virtual-instances/create-an-instance/13-add-user-data.png" />
    </Frame>

    <p>A password for connecting to a Linux instance [from the Customer Portal](/cloud/virtual-instances/connect/connect-to-your-instance-via-control-panel) or [via SSH](/cloud/virtual-instances/connect/connect-via-ssh) can be set via **User data**. Insert the following code into the field, replacing `my-password` with the chosen password:</p>

    ```yaml theme={null}
    #cloud-config 
    password: my-password 
    chpasswd: { expire: False } 
    ssh_pwauth: True
    ```

    <Info>
      **Info**

      On Cloud instances with Windows OS, the `password` parameter cannot be used in both the "Access" and "User data" fields. Since the "Access" field is required, configuring user data on Windows instances is not possible. See the allowed parameters in the [API reference](/api-reference/cloud#tag/Instances/operation/InstanceCreateSetV2.post).
    </Info>

    <Accordion title="Configure a password hash">
      A password hash — a machine-readable set of symbols — can be configured to protect the actual password from being exposed. To generate a hash, use the following Python script:

      ```
      #!/usr/bin/env python3  
      \# based on [https://stackoverflow.com/a/17992126/117471](https://stackoverflow.com/a/17992126/117471)\# pip3 install passlib  
      import sys  
      from getpass import getpass  
      from passlib.hash import sha512_crypt  
      passwd = input() if not sys.stdin.isatty() else getpass()  
      print(sha512_crypt.hash(passwd, rounds = 5000 ))
      ```
    </Accordion>

    <Warning>
      **Warning**

      When an instance is only in a private subnet, DHCP must be enabled in the subnet settings to allow password-based login.
    </Warning>

    ### Tags

    <p>Enable **Add tags** to add a key-value pair as metadata for the Virtual Instance.</p>

    ### Placement

    <p>Enable **Add to placement group** to determine how to place multiple instances.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/15-add-placement-group.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=23d01898320c55fe730285cdfca06b84" alt="The field to add the VM to a placement group" width="630" height="453" data-path="images/docs/cloud/virtual-instances/create-an-instance/15-add-placement-group.png" />
    </Frame>

    <Accordion title="Types of placement groups">
      <p>Instances can be placed in one of three types of groups:</p>

      * **Affinity** groups assemble virtual machines on the same hardware. Machines launched in one affinity group will exchange data faster because they are located on the same server.
      * **Anti-Affinity** groups work the opposite way: all Virtual Machines in this group will be separated across different physical hardware. This increases fault tolerance of a cluster: Even if something goes wrong with one server, machines on the other servers will remain available.
      * **Soft Anti-Affinity** groups encourage, but don't strictly enforce, the separation of Virtual Machines. Unlike a strict anti-affinity policy, where machines may never be placed together, soft anti-affinity allows placement on the same hardware when it is necessary due to factors like resource constraints or high demand. It is suitable for users who want to use the anti-affinity policy by default while also avoiding machine creation failures if an unused host is not found.
    </Accordion>

    <p>Add the instance to an existing placement group or create a new one by clicking **Add placement group**.</p>

    ## Step 8. Specify the number of instances

    <p>Indicate how many instances with the same configuration are needed. No more than 10 instances can be created at a time.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/yrEa6OGy4Q_zWlXb/images/docs/cloud/virtual-instances/create-an-instance/16-number-of-instances.png?fit=max&auto=format&n=yrEa6OGy4Q_zWlXb&q=85&s=f2cd1535c52591f68d47390f018c3ec0" alt="The field to set the number of instances" width="682" height="225" data-path="images/docs/cloud/virtual-instances/create-an-instance/16-number-of-instances.png" />
    </Frame>

    <p>Set the **Name Template** for the instances. Select one of the available options from the dropdown:</p>

    * `{ip_octets}` — the placeholder is replaced with the last two octets of the assigned IP address (for example, `my-vm-{ip_octets}` becomes `my-vm-10-0-5`).
    * `{instance_role}` — the placeholder is replaced with the value of the `instance_role` metadata tag set in the **Add tags** section.

    ## Step 9. Create the instance

    <p>Click **Create Instance**.</p>

    <p>The instance transitions to the **Building** status while resources are being allocated.</p>

    <p>After that, the instance moves to the **Power on** status and is ready to use.</p>
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>Creating a Linux VM via the REST API consists of three API calls: list available images, list flavors, then submit the create request. The instance is reachable via SSH within about 30 seconds of the task finishing.</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required, along with a [project ID](/api-reference/cloud/projects/list-projects) and [region ID](/api-reference/cloud/regions/list-regions).
    </Info>

    <p>Open a bash terminal and set these as 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}"
    ```

    ## Add an SSH key

    <p>If the project does not yet have an SSH key, register one before creating the instance. The create call references the key by name, not by ID.</p>

    | Parameter    | Required | Description                                                           |
    | ------------ | -------- | --------------------------------------------------------------------- |
    | `name`       | Yes      | Identifier used as `ssh_key_name` in the create call.                 |
    | `public_key` | No       | An RSA or Ed25519 public key string. Omit to generate a new key pair. |

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        ssh_key = client.cloud.ssh_keys.create(
            name="my-key",
            public_key="ssh-ed25519 AAAA... user@host",
        )
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        sshKeyCreated, err := client.Cloud.SSHKeys.New(context.TODO(), cloud.SSHKeyNewParams{
            Name: "my-ssh-key",
        })
        if err != nil {
            panic(err.Error())
        }
        fmt.Printf("%+v\n", sshKeyCreated.ID)
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X POST "https://api.gcore.com/cloud/v1/ssh_keys/$GCORE_CLOUD_PROJECT_ID" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "name": "my-key",
            "public_key": "ssh-ed25519 AAAA... user@host"
          }'
        ```

        Response:

        ```json theme={null}
        {
          "id": "36a7a97a-0672-4911-8f2b-92cd4e5b0d91",
          "name": "my-key",
          "state": "ACTIVE"
        }
        ```
      </Tab>
    </Tabs>

    <p>When `public_key` is omitted, the response includes a `private_key` field - save it immediately, as it is not shown again.</p>

    ## Quickstart

    <p>Complete scripts for the full flow. See the step-by-step section below for details on each call.</p>

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

        client = Gcore()

        # Step 1. Find Ubuntu 22.04 image
        images = client.cloud.instances.images.list()
        image = next(img for img in images.results if "ubuntu-22.04" in img.name and "x64" in img.name)
        print(f"Image: {image.name} ({image.id}), min_disk={image.min_disk} GiB")

        # Step 2. Pick a flavor (IDs are region-specific; this selects 2 vCPU / 4 GB)
        flavors = client.cloud.instances.flavors.list()
        flavor = next(f for f in flavors.results if f.vcpus == 2 and f.ram == 4096)
        print(f"Flavor: {flavor.flavor_id} ({flavor.vcpus} vCPU / {flavor.ram // 1024} GB RAM)")

        # Step 3. Create instance and wait (create_and_poll polls automatically)
        instance = client.cloud.instances.create_and_poll(
            name="my-vm",
            flavor=flavor.flavor_id,
            ssh_key_name="my-key",
            interfaces=[{"type": "external"}],
            volumes=[{
                "source": "image",
                "image_id": image.id,
                "size": max(20, image.min_disk),
                "boot_index": 0,
            }],
        )
        print(f"Instance ID: {instance.id}, Status: {instance.status}")

        # Step 4. Wait for ACTIVE status — handled automatically by create_and_poll()

        # Step 5. Get public IP
        for ifaces in instance.addresses.values():
            for iface in ifaces:
                print(f"Public IP: {iface.addr}")

        # Step 6. Connect via SSH
        for ifaces in instance.addresses.values():
            for iface in ifaces:
                print(f"ssh ubuntu@{iface.addr}")
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "os"
            "strings"

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

        func main() {
            client := gcore.NewClient()

            // Step 1. Find Ubuntu 22.04 image
            imageList, err := client.Cloud.Instances.Images.List(context.TODO(), cloud.InstanceImageListParams{})
            if err != nil {
                panic(err.Error())
            }
            var imageID string
            for _, img := range imageList.Results {
                if strings.Contains(img.Name, "ubuntu-22.04") && strings.Contains(img.Name, "x64") {
                    imageID = img.ID
                    fmt.Printf("Image: %s (%s)\n", img.Name, img.ID)
                    break
                }
            }

            // Step 2. Pick a flavor (IDs are region-specific; this selects 2 vCPU / 4 GB)
            flavorList, err := client.Cloud.Instances.Flavors.List(context.TODO(), cloud.InstanceFlavorListParams{})
            if err != nil {
                panic(err.Error())
            }
            var flavorID string
            for _, f := range flavorList.Results {
                if f.Vcpus == 2 && f.Ram == 4096 {
                    flavorID = f.FlavorID
                    fmt.Printf("Flavor: %s (%d vCPU / %d MB RAM)\n", f.FlavorID, f.Vcpus, f.Ram)
                    break
                }
            }

            // Step 3. Create instance and wait (NewAndPoll polls automatically)
            instance, err := client.Cloud.Instances.NewAndPoll(context.TODO(), cloud.InstanceNewParams{
                Flavor:       flavorID,
                NameTemplate: gcore.String("my-vm-{ip_octets}"),
                SSHKeyName:   gcore.String("my-key"),
                Interfaces: []cloud.InstanceNewParamsInterfaceUnion{{
                    OfExternal: &cloud.InstanceNewParamsInterfaceExternal{},
                }},
                Volumes: []cloud.InstanceNewParamsVolumeUnion{{
                    OfImage: &cloud.InstanceNewParamsVolumeImage{
                        ImageID:   imageID,
                        Size:      gcore.Int(20),
                        BootIndex: gcore.Int(0),
                    },
                }},
            })
            if err != nil {
                panic(err.Error())
            }
            fmt.Printf("Instance ID: %s, Status: %s\n", instance.ID, instance.Status)

            // Step 4. Wait for ACTIVE status — handled automatically by NewAndPoll()

            // Step 5. Get public IP
            // Step 6. Connect via SSH
            for _, addrs := range instance.Addresses {
                for _, addr := range addrs {
                    fmt.Printf("ssh ubuntu@%s\n", addr.Addr)
                }
            }
        }
        ```
      </Tab>
    </Tabs>

    ## Step-by-step

    <p>Each step below explains what the call does, which parameters matter, and what the response looks like. Use this section to understand the flow or to debug a specific step.</p>

    <Accordion title="Show all steps">
      ### Step 1. Choose an OS image

      The image determines the operating system and sets the minimum boot volume size for Step 3.

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

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

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

          import (
              "context"
              "fmt"

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

          func main() {
              client := gcore.NewClient()
              imageList, err := client.Cloud.Instances.Images.List(context.TODO(), cloud.InstanceImageListParams{})
              if err != nil {
                  panic(err.Error())
              }
              fmt.Printf("%+v\n", imageList.Count)
          }
          ```
        </Tab>

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

          Response:

          ```json theme={null}
          {
            "results": [
              {
                "id": "e460e48c-6836-447e-bc9c-16fc4225d318",   // save as IMAGE_ID
                "name": "ubuntu-22.04-x64",
                "os_distro": "ubuntu",
                "os_version": "22.04",
                "architecture": "x86_64",
                "min_disk": 5
              }
            ]
          }
          ```
        </Tab>
      </Tabs>

      Save the `id` of the chosen image. The `min_disk` value is the minimum boot volume size in GiB.

      ```bash theme={null}
      export IMAGE_ID="{IMAGE_ID}"
      ```

      ### Step 2. Choose a flavor

      A [flavor](/api-reference/cloud/flavors/list-flavors) sets the vCPU count and RAM for the instance. IDs are region-specific — run this call to see what's available in the selected region.

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          flavors = client.cloud.instances.flavors.list()
          for f in flavors.results:
              print(f.flavor_id, f"{f.vcpus} vCPU / {f.ram // 1024} GB RAM")
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          instanceFlavorList, err := client.Cloud.Instances.Flavors.List(context.TODO(), cloud.InstanceFlavorListParams{})
          if err != nil {
              panic(err.Error())
          }
          fmt.Printf("%+v\n", instanceFlavorList.Count)
          ```
        </Tab>

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

          Response:

          ```json theme={null}
          {
            "results": [
              {"flavor_id": "g2-standard-2-4", "vcpus": 2, "ram": 4096},
              {"flavor_id": "g2-standard-4-8", "vcpus": 4, "ram": 8192}
            ]
          }
          ```
        </Tab>
      </Tabs>

      ```bash theme={null}
      export FLAVOR_ID="{FLAVOR_ID}"   # replace with a flavor_id from the response above
      ```

      ### Step 3. Create the instance

      The instance is created with a public interface - `"type": "external"` assigns a public IPv4 address automatically, without a separate floating IP.

      | Parameter              | Required | Description                                                           |
      | ---------------------- | -------- | --------------------------------------------------------------------- |
      | `name`                 | Yes      | Display name for the instance.                                        |
      | `flavor`               | Yes      | Flavor ID string from Step 2.                                         |
      | `ssh_key_name`         | Yes      | Name of an existing SSH key in the project.                           |
      | `interfaces[].type`    | Yes      | `"external"` for a public IP. Use `"subnet"` for a private interface. |
      | `volumes[].source`     | Yes      | `"image"` to boot from an image.                                      |
      | `volumes[].size`       | Yes      | Boot volume size in GiB. Must be at least `min_disk` from Step 1.     |
      | `volumes[].boot_index` | Yes      | Set to `0` for the boot volume.                                       |

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          instance = client.cloud.instances.create_and_poll(
              name="my-vm",
              flavor=os.environ["FLAVOR_ID"],
              ssh_key_name="my-key",
              interfaces=[{"type": "external"}],
              volumes=[{
                  "source": "image",
                  "image_id": os.environ["IMAGE_ID"],
                  "size": 20,
                  "boot_index": 0,
              }],
          )
          instance_id = instance.id
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          instance, err := client.Cloud.Instances.NewAndPoll(context.TODO(), cloud.InstanceNewParams{
              Flavor:       os.Getenv("FLAVOR_ID"),
              NameTemplate: gcore.String("my-vm-{ip_octets}"),
              SSHKeyName:   gcore.String("my-key"),
              Interfaces: []cloud.InstanceNewParamsInterfaceUnion{{
                  OfExternal: &cloud.InstanceNewParamsInterfaceExternal{},
              }},
              Volumes: []cloud.InstanceNewParamsVolumeUnion{{
                  OfImage: &cloud.InstanceNewParamsVolumeImage{
                      ImageID:   os.Getenv("IMAGE_ID"),
                      Size:      gcore.Int(20),
                      BootIndex: gcore.Int(0),
                  },
              }},
          })
          if err != nil {
              panic(err.Error())
          }
          // instance is ACTIVE when NewAndPoll returns
          instanceID := instance.ID
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl -X POST "https://api.gcore.com/cloud/v2/instances/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID" \
            -H "Authorization: APIKey $GCORE_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{
              "name": "my-vm",
              "flavor": "'"$FLAVOR_ID"'",
              "ssh_key_name": "my-key",
              "interfaces": [{"type": "external"}],
              "volumes": [
                {
                  "source": "image",
                  "image_id": "'"$IMAGE_ID"'",
                  "size": 20,
                  "boot_index": 0
                }
              ]
            }'
          ```

          Response:

          ```json theme={null}
          {
            "tasks": ["d4a4b10e-5d22-4c23-8e09-f7a3e1c1a999"]   // save as TASK_ID
          }
          ```
        </Tab>
      </Tabs>

      ### Step 4. Wait for the instance to be ready

      Instance creation is asynchronous. The Python SDK `create_and_poll()` and the Go SDK `NewAndPoll()` in Step 3 handle polling automatically and return only when the instance is ACTIVE. For curl, poll `GET /cloud/v1/tasks/{task_id}` every 5 seconds until `state` is `FINISHED`.

      <Tabs>
        <Tab title="Python SDK">
          `create_and_poll()` in Step 3 polls automatically. `instance` is ACTIVE when Step 3 returns.
        </Tab>

        <Tab title="Go SDK">
          `NewAndPoll()` in Step 3 polls automatically. `instance` is ACTIVE when Step 3 returns.
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl "https://api.gcore.com/cloud/v1/tasks/$TASK_ID" \
            -H "Authorization: APIKey $GCORE_API_KEY"
          ```

          While provisioning:

          ```json theme={null}
          {"state": "RUNNING", "task_type": "create_vm"}
          ```

          When complete:

          ```json theme={null}
          {
            "state": "FINISHED",
            "created_resources": {
              "instances": ["169942e0-9b53-42df-95ef-1a8b6525c2bd"],   // save as INSTANCE_ID
              "volumes": ["726ecfcc-7fd0-4e30-a86e-7892524aa483"]
            }
          }
          ```
        </Tab>
      </Tabs>

      ```bash theme={null}
      export INSTANCE_ID="{INSTANCE_ID}"
      ```

      ### Step 5. Get the public IP address

      The `addresses` field on the instance object contains the public IP assigned at creation.

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          # instance.addresses is populated on the object returned by create_and_poll()
          for ifaces in instance.addresses.values():
              for iface in ifaces:
                  print(f"IP: {iface.addr}")
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          instance, err := client.Cloud.Instances.Get(
              context.TODO(),
              "instance_id",
              cloud.InstanceGetParams{},
          )
          if err != nil {
              panic(err.Error())
          }
          fmt.Printf("%+v\n", instance.ID)
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl "https://api.gcore.com/cloud/v1/instances/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$INSTANCE_ID" \
            -H "Authorization: APIKey $GCORE_API_KEY"
          ```

          Response:

          ```json theme={null}
          {
            "status": "ACTIVE",
            "addresses": {
              "pub_net": [
                {"addr": "203.0.113.42", "type": "fixed"}
              ]
            }
          }
          ```
        </Tab>
      </Tabs>

      The keys in `addresses` are network names that vary by region (typically `"pub_net"` for public interfaces), so iterate over all keys when parsing programmatically.

      ### Step 6. Connect via SSH

      Connect using the public IP from Step 5. Replace `ubuntu` with the default user for the chosen OS image.

      ```bash theme={null}
      ssh -i ~/.ssh/my-key ubuntu@203.0.113.42
      ```

      | OS          | Default user |
      | ----------- | ------------ |
      | Ubuntu      | `ubuntu`     |
      | Debian      | `debian`     |
      | CentOS      | `centos`     |
      | Rocky Linux | `rocky`      |
      | Fedora      | `fedora`     |

      The default security group is applied automatically, allowing inbound SSH (port 22) and ICMP.
    </Accordion>

    ## Filter images by OS

    <p>To list only Gcore-managed public images, add the `visibility=public` parameter.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        images = client.cloud.instances.images.list(visibility="public")
        for img in images.results:
            print(img.id, img.name, img.os_distro, img.os_version)
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        imageList, err := client.Cloud.Instances.Images.List(context.TODO(), cloud.InstanceImageListParams{
            Visibility: cloud.InstanceImageListParamsVisibilityPublic,
        })
        if err != nil {
            panic(err.Error())
        }
        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/v1/images/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID?visibility=public" \
          -H "Authorization: APIKey $GCORE_API_KEY"
        ```
      </Tab>
    </Tabs>

    ## Clean up

    <p>To delete the instance and its boot volume in a single call, pass the volume ID in the `volumes` query parameter.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        boot_volume_id = instance.volumes[0].id
        client.cloud.instances.delete_and_poll(
            instance_id=instance.id,
            volumes=boot_volume_id,
        )
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        taskIDList, err := client.Cloud.Instances.Delete(
            context.TODO(),
            "instance_id",
            cloud.InstanceDeleteParams{},
        )
        if err != nil {
            panic(err.Error())
        }
        fmt.Printf("%+v\n", taskIDList.Tasks)
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X DELETE \
          "https://api.gcore.com/cloud/v1/instances/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$INSTANCE_ID?volumes=$BOOT_VOLUME_ID" \
          -H "Authorization: APIKey $GCORE_API_KEY"
        ```

        Response:

        ```json theme={null}
        {"tasks": ["ef3b0d34-..."]}
        ```

        Poll `GET /cloud/v1/tasks/{task_id}` every 5 seconds until `state` is `FINISHED`.
      </Tab>
    </Tabs>

    <p>If a floating IP was assigned to the instance, add `delete_floatings=true` to the query string.</p>
  </MethodSection>

  <MethodSection id="terraform" label="Terraform">
    <p>Declare a Virtual Machine and its boot volume together. The [Terraform provider](/developer-tools/terraform/overview) models these as two separate resources — [`gcore_cloud_volume`](https://registry.terraform.io/providers/G-Core/gcore/latest/docs/resources/cloud_volume) and [`gcore_cloud_instance`](https://registry.terraform.io/providers/G-Core/gcore/latest/docs/resources/cloud_instance) — provisioned in order.</p>

    ## Create a VM

    <p>Declares a boot volume from an Ubuntu 22.04 image and a VM with a public IPv4 interface. `ssh_key_name` takes the name of an [SSH key](#add-an-ssh-key) registered in the project.</p>

    ```hcl theme={null}
    resource "gcore_cloud_volume" "boot" {
      project_id = var.project_id
      region_id  = var.region_id
      name       = "my-boot-volume"
      source     = "image"
      image_id   = var.image_id
      size       = 20
      type_name  = "ssd_hiiops"
    }

    resource "gcore_cloud_instance" "example" {
      project_id   = var.project_id
      region_id    = var.region_id
      flavor       = "g2-standard-2-4"
      name         = "my-vm"
      ssh_key_name = "my-keypair"

      volumes = [{ volume_id = gcore_cloud_volume.boot.id }]

      interfaces = [{
        type      = "external"
        ip_family = "ipv4"
      }]
    }

    output "instance_id" {
      value = gcore_cloud_instance.example.id
    }

    output "public_ip" {
      value = gcore_cloud_instance.example.addresses
    }
    ```

    <p>To find the image ID for the region, query the [Images API](/api-reference/cloud#tag/Images/operation/ImageListForProjectRegion.get):</p>

    ```bash theme={null}
    curl "https://api.gcore.com/cloud/v1/images/{project_id}/{region_id}?visibility=public&os_distro=ubuntu" \
      -H "Authorization: APIKey $GCORE_API_KEY" | jq '.results[] | select(.name | contains("22.04")) | {id, name}'
    ```

    <Warning>
      `gcore_cloud_volume` is an independent resource. Removing `gcore_cloud_instance.example` from the configuration does not delete its boot volume. To deprovision both, remove `gcore_cloud_volume.boot` from the configuration as well, then run `terraform apply`.
    </Warning>

    ## Add an SSH key

    <p>If the project does not yet have an SSH key, declare it as a Terraform resource. SSH keys are project-wide and do not require `region_id`.</p>

    ```hcl theme={null}
    resource "gcore_cloud_ssh_key" "my_key" {
      project_id = var.project_id
      name       = "my-keypair"
      public_key = "ssh-ed25519 AAAA... user@example.com"
    }

    resource "gcore_cloud_instance" "example" {
      # ...
      ssh_key_name = gcore_cloud_ssh_key.my_key.name
    }
    ```

    <p>When `public_key` is omitted, Gcore generates a key pair. The private key is returned once in the response and is not stored — save it before applying.</p>

    ## Optional fields

    | Field                      | Description                                                                                                                    |
    | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
    | `user_data`                | Base64-encoded cloud-init script that runs at first boot.                                                                      |
    | `security_groups`          | List of firewall group IDs. If omitted, the default security group is applied.                                                 |
    | `username` / `password_wo` | Creates a Linux user or sets the Windows Admin password at boot.                                                               |
    | `vm_state`                 | `"active"` (default) to start immediately; `"stopped"` to keep the VM off.                                                     |
    | `tags`                     | Map of key-value labels attached to the instance.                                                                              |
    | `name_template`            | Auto-generates the name from the public IP, e.g. `"my-vm-{ip_octets}"`. Use instead of `name` when dynamic naming is required. |

    <p>Example with `user_data` and `tags`:</p>

    ```hcl theme={null}
    resource "gcore_cloud_instance" "example" {
      # ... required fields ...

      user_data = base64encode(<<-EOF
        #cloud-config
        password: your-password
        chpasswd: { expire: false }
        ssh_pwauth: true
      EOF
      )

      tags = {
        env  = "production"
        team = "backend"
      }
    }
    ```

    ## Delete a VM

    <Warning>
      `gcore_cloud_volume` is an independent resource. Removing `gcore_cloud_instance.example` from the configuration does not delete its boot volume. To deprovision both, remove `gcore_cloud_volume.boot` from the configuration as well, then run `terraform apply`.
    </Warning>

    <p>Remove both `gcore_cloud_instance.example` and `gcore_cloud_volume.boot` from the configuration, then run `terraform apply` — Terraform deletes the instance first, then the volume.</p>

    ```hcl theme={null}
    # Remove or comment out both blocks:
    # resource "gcore_cloud_instance" "example" {
    #   name       = "my-vm"
    #   flavor     = "g2-standard-2-4"
    #   ...
    # }
    # resource "gcore_cloud_volume" "boot" {
    #   name      = "my-boot-volume"
    #   source    = "image"
    #   ...
    # }
    ```

    ```bash theme={null}
    terraform apply
    ```

    ## Import a VM

    <p>Import the instance and its boot volume separately to bring existing infrastructure under configuration management:</p>

    ```bash theme={null}
    terraform import gcore_cloud_instance.example '<project_id>/<region_id>/<instance_id>'
    terraform import gcore_cloud_volume.boot '<project_id>/<region_id>/<volume_id>'
    ```

    <p>To find the boot volume ID, look for `"is_root_volume": true` in the instance details:</p>

    ```bash theme={null}
    curl "https://api.gcore.com/cloud/v1/instances/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$INSTANCE_ID" \
      -H "Authorization: APIKey $GCORE_API_KEY" | jq '.volumes[] | select(.is_root_volume == true) | .id'
    ```
  </MethodSection>
</MethodSwitch>
