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

# Upload a custom image

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>Use the [Gcore Customer Portal](https://portal.gcore.com) to upload a custom image from a public URL. Before uploading, verify it meets the format, driver, and package requirements below.</p>

    ### Supported formats

    <p>The uploaded image must be in one of the following formats:</p>

    |                 |                              |
    | --------------- | ---------------------------- |
    | raw             | Uncompressed image format    |
    | vhd / vhdx      | Microsoft Hyper-V formats    |
    | vdi             | VirtualBox disk image        |
    | ploop           | Parallels storage format     |
    | qcow2           | QEMU copy-on-write format    |
    | aki / ari / ami | Amazon Machine Image formats |

    ### Drivers and packages

    <p>Images downloaded from other cloud providers already include both VirtIO SCSI drivers and the `cloud-init` package. For custom-built images, install and configure both before uploading.</p>

    ### Start modes

    <p>The **Instance quick start** checkbox controls how Virtual Instances are deployed from the image. Choose based on speed requirements and whether it needs to remain deletable.</p>

    | Deployment mode | Standard                                | Quick start                                                                                               |
    | --------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
    | Technology      | RBD copy-on-write                       | RBD copy-on-write                                                                                         |
    | Image data      | All data is copied to a separate volume | Image data is not copied when reading or writing — all changes are applied directly to the original image |
    | Format          | qcow2                                   | raw                                                                                                       |
    | Resource usage  | Resource-intensive                      | Less resources used                                                                                       |
    | Image deletion  | The image can be deleted at any time    | The image cannot be deleted while Virtual Instances created from it are active                            |

    ## Upload an image

    <p>Once the image meets the requirements above, upload it using the following steps.</p>

    ### Open the upload form

    <p>1. In the **Cloud** menu, select the desired project and region.</p>

    <p>2. In the sidebar, navigate to **Images** and click **Import via URL**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/-tGG7PWTFMtYIhJ9/images/docs/cloud/images/upload-an-image-to-the-storage/click-import-via-url-new.png?fit=max&auto=format&n=-tGG7PWTFMtYIhJ9&q=85&s=d55e278009eda12a011969bd7afaf69c" alt="Images section with the Import via URL button" width="1440" height="400" data-path="images/docs/cloud/images/upload-an-image-to-the-storage/click-import-via-url-new.png" />
    </Frame>

    ### Configure image settings

    <p>1. Select the Resource type:</p>

    * Virtual Instances
    * Bare Metal
    * Virtual GPU Clusters
    * Bare Metal GPU Clusters

    <p>2. Enter a name and the public URL from which to download the image.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/-tGG7PWTFMtYIhJ9/images/docs/cloud/images/upload-an-image-to-the-storage/enter-url-of-your-image-new.png?fit=max&auto=format&n=-tGG7PWTFMtYIhJ9&q=85&s=ed394ef80521d49481b23167f755ebc1" alt="Upload an Image form with the image name and URL fields filled in" width="432" height="382" data-path="images/docs/cloud/images/upload-an-image-to-the-storage/enter-url-of-your-image-new.png" />
    </Frame>

    <p>3. Choose the architecture of the image based on the processor type where it will run: select **x86-64** for traditional CISC processors like Intel or AMD, or **ARM** for RISC-based processors such as ARM CPUs.</p>

    <p>4. (Optional) Select the **Instance quick start** checkbox to use raw format for faster deployment — the [start modes](#start-modes) comparison table covers the trade-offs. Without this option, the image is copied to a separate qcow2 volume before use.</p>

    <Note>
      When Instance quick start is enabled, the image cannot be deleted while Virtual Instances created from it are active.
    </Note>

    <p>5. In the **Permission to use an SSH key in instances** field, choose the permission level: **Allow** makes SSH key usage optional, **Deny** prohibits it entirely, and **Required** mandates SSH key usage for all instances created from this image. SSH-key authorization is recommended for security.</p>

    <p>6. In the **Operating system installed on the image** field, select the OS type: **Linux** or **Windows**.</p>

    <p>7. In the **Type of firmware with which to boot the guest** field, select **UEFI** or **BIOS**. Bare Metal servers and GPU Bare Metal clusters support UEFI only. Virtual Instances and GPU virtual clusters support both — choose UEFI for modern systems or BIOS for legacy setups.</p>

    <p>8. In the **Virtual chipset type** field, choose the virtual motherboard chipset:</p>

    * **q35** — modern PCIe-based chipset, recommended for current operating systems and UEFI
    * **i440** — legacy PCI-based chipset, for older operating systems with limited PCIe support

    <p>9. (Optional) Select the **Add tags** checkbox, then enter **Key** and **Value** pairs to attach [tags](/cloud/tags) to the image.</p>

    ### Upload

    <p>1. Click the **Upload** button.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/-tGG7PWTFMtYIhJ9/images/docs/cloud/images/upload-an-image-to-the-storage/click-upload-new.png?fit=max&auto=format&n=-tGG7PWTFMtYIhJ9&q=85&s=e5e1be69cf038319c523c03d98563753" alt="Upload an Image form with the Upload button" width="431" height="382" data-path="images/docs/cloud/images/upload-an-image-to-the-storage/click-upload-new.png" />
    </Frame>

    <p>2. Confirm it appears in the **Images** list. Gcore fetches and stores it in the selected region, making it available for creating Virtual Instances, Bare Metal servers, and GPU clusters.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/-tGG7PWTFMtYIhJ9/images/docs/cloud/images/upload-an-image-to-the-storage/images-list-new.png?fit=max&auto=format&n=-tGG7PWTFMtYIhJ9&q=85&s=2f78a77c1e1d9272e84da9396bf1cf98" alt="Images list showing a newly added custom image" width="1440" height="400" data-path="images/docs/cloud/images/upload-an-image-to-the-storage/images-list-new.png" />
    </Frame>
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>Provide a public URL — Gcore fetches and stores the image in the selected region. Images must be in raw, vhd, vhdx, vdi, ploop, qcow2, ami, aki, or ari format. Once the import task finishes, the image is available for creating Virtual Instances and Bare Metal servers.</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).
    </Info>

    <p>Set the following 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}"
    ```

    ## Upload an image

    <p>The [upload image](/api-reference/cloud#tag/Images/operation/ImageDownloadViewSet.post) endpoint fetches the file from the provided URL and stores it in the region. It becomes available once the download task completes.</p>

    | Parameter          | Required | Description                                                                                                       |
    | ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------- |
    | `name`             | Yes      | Display name for the image                                                                                        |
    | `url`              | Yes      | Public URL of the image file in one of the supported formats: raw, vhd, vhdx, vdi, ploop, qcow2, ami, aki, or ari |
    | `os_type`          | No       | OS family — `linux` or `windows`; defaults to `linux`                                                             |
    | `architecture`     | No       | CPU architecture — `x86_64` or `aarch64`; defaults to `x86_64`                                                    |
    | `ssh_key`          | No       | SSH key policy — `allow`, `deny`, or `required`; defaults to `allow`                                              |
    | `cow_format`       | No       | Set to `true` to enable VM quick start (raw format, image-backed VMs); defaults to `false` (qcow2, full copy)     |
    | `is_baremetal`     | No       | Set to `true` to use the image with Bare Metal servers; defaults to `false`                                       |
    | `hw_firmware_type` | No       | Boot firmware — `bios` or `uefi`                                                                                  |

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

        client = Gcore()

        image = client.cloud.instances.images.upload_and_poll(
            name="my-custom-image",
            url="https://example.com/images/ubuntu-24.04-custom.qcow2",
            os_type="linux",
            architecture="x86_64",
            ssh_key="allow",
        )
        print(f"Image ID:   {image.id}")    # save as IMAGE_ID
        print(f"Status:     {image.status}")
        print(f"Format:     {image.disk_format}")
        print(f"Size:       {image.size} bytes")
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        import (
            "context"
            "fmt"
            "log"

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

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

        image, err := client.Cloud.Instances.Images.UploadAndPoll(ctx, cloud.InstanceImageUploadParams{
            Name:         "my-custom-image",
            URL:          "https://example.com/images/ubuntu-24.04-custom.qcow2",
            OsType:       cloud.InstanceImageUploadParamsOsTypeLinux,
            Architecture: cloud.InstanceImageUploadParamsArchitectureX86_64,
            SSHKey:       cloud.InstanceImageUploadParamsSSHKeyAllow,
        })
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Image ID: %s\n", image.ID)    // save as IMAGE_ID
        fmt.Printf("Status:   %s\n", image.Status)
        fmt.Printf("Format:   %s\n", image.DiskFormat)
        fmt.Printf("Size:     %d bytes\n", image.Size)
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X POST "https://api.gcore.com/cloud/v1/downloadimage/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "name": "my-custom-image",
            "url": "https://example.com/images/ubuntu-24.04-custom.qcow2",
            "os_type": "linux",
            "architecture": "x86_64",
            "ssh_key": "allow"
          }'
        ```

        Response:

        ```json theme={null}
        {
          "tasks": ["b63f0fff-1234-5678-abcd-ef0123456789"]
        }
        ```

        Poll `GET https://api.gcore.com/cloud/v1/tasks/{task_id}` every 5 seconds until `state` is `FINISHED`. The image ID is in `created_resources.images[0]`.
      </Tab>
    </Tabs>

    <p>Uploaded images are assigned `visibility: "shared"` — visible within the project and region but not publicly available. To list, update, or delete images, open [Manage custom images](/cloud/images/manage-custom-images).</p>
  </MethodSection>

  <MethodSection id="terraform" label="Terraform">
    <p>Provide a public URL in the `gcore_cloud_instance_image` resource — Gcore fetches and stores it in the specified region. Supported formats: raw, vhd, vhdx, vdi, ploop, qcow2, ami, aki, or ari. Once created, the resource ID can be referenced in instance or volume resources.</p>

    ## Upload an image

    <p>Upload from a public URL. After apply, the resource is available in the region with `visibility: "shared"`.</p>

    ```hcl theme={null}
    resource "gcore_cloud_instance_image" "example" {
      project_id   = var.project_id
      region_id    = var.region_id
      name         = "my-custom-image"
      url          = "https://example.com/images/ubuntu-24.04-custom.qcow2"
      os_type      = "linux"       # "linux" or "windows"
      architecture = "x86_64"     # "x86_64" or "aarch64"
      ssh_key      = "allow"      # "allow", "deny", or "required"
      # cow_format   = true       # enable VM quick start (image-backed VMs)
      # is_baremetal = true       # set to true for Bare Metal images

      # terraform import gcore_cloud_instance_image.example '<project_id>/<region_id>/<image_id>'
    }

    output "image_id" {
      value = gcore_cloud_instance_image.example.id
    }
    ```

    ## Replace an image

    <Warning>Changing `url` or `os_version` destroys the existing image and uploads a new one — the image ID changes. Changing `name` or `tags` updates in place without replacement.</Warning>

    ```hcl theme={null}
    resource "gcore_cloud_instance_image" "example" {
      project_id   = var.project_id
      region_id    = var.region_id
      name         = "my-custom-image-v2"  # changed
      url          = "https://example.com/images/ubuntu-24.04-custom.qcow2"
      os_type      = "linux"
      architecture = "x86_64"
      ssh_key      = "allow"
    }
    ```

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

    ## Delete an image

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

    ```hcl theme={null}
    # Remove or comment out this block:
    # resource "gcore_cloud_instance_image" "example" {
    #   name         = "my-custom-image"
    #   url          = "https://example.com/images/ubuntu-24.04-custom.qcow2"
    #   project_id   = var.project_id
    #   region_id    = var.region_id
    # }
    ```

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