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

# Configure cluster autoscaling

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>The Cluster Autoscaler adjusts cluster size based on workload demand. When pods are pending due to insufficient resources, the autoscaler adds nodes. When nodes are underutilized, it removes them. Autoscaling operates within the min/max limits set per pool.</p>

    <Info>
      The Cluster Autoscaler scales based on CPU and memory utilization only. GPU utilization is not used as a scaling signal.
    </Info>

    <Steps>
      <Step title="Navigate to the Advanced settings tab">
        <Frame>
          <img src="https://mintcdn.com/gcore/c2W3cwtGjSbFG_V1/images/docs/edge-ai/managed-kubernetes/configure-cluster-autoscaling/configure-cluster-autoscaling-image1.png?fit=max&auto=format&n=c2W3cwtGjSbFG_V1&q=85&s=e4222cb42b64d3cbaedebed6c100dda7" alt="Advanced settings tab showing OIDC authentication and Cluster Autoscaler sections" width="1024" height="438" data-path="images/docs/edge-ai/managed-kubernetes/configure-cluster-autoscaling/configure-cluster-autoscaling-image1.png" />
        </Frame>
      </Step>

      <Step title="Expand Cluster Autoscaler" />

      <Step title="Configure the parameters and click Save changes">
        <Frame>
          <img src="https://mintcdn.com/gcore/c2W3cwtGjSbFG_V1/images/docs/edge-ai/managed-kubernetes/configure-cluster-autoscaling/configure-cluster-autoscaling-image2.png?fit=max&auto=format&n=c2W3cwtGjSbFG_V1&q=85&s=2583551cee87e52ca06db962a68be225" alt="Cluster Autoscaler settings form showing scale-down delays, utilization threshold, and toggle options" width="1024" height="523" data-path="images/docs/edge-ai/managed-kubernetes/configure-cluster-autoscaling/configure-cluster-autoscaling-image2.png" />
        </Frame>
      </Step>
    </Steps>

    | Parameter                        | Description                                                 | Default     |
    | -------------------------------- | ----------------------------------------------------------- | ----------- |
    | Cluster scan interval            | How often the autoscaler checks for pending pods            | 10 seconds  |
    | Cluster expander                 | Strategy for selecting which node group to scale            | random      |
    | Max node provision time          | Maximum time to wait for a node to become ready             | 15 minutes  |
    | New pod scale up delay           | Wait time before triggering scale-up for new pods           | 0 seconds   |
    | Post-addition scale-down delay   | Wait time after adding a node before considering scale-down | 5 minutes   |
    | Post-deletion scale-down delay   | Wait time after deleting a node before the next deletion    | 0 seconds   |
    | Unneeded node delay              | How long a node must be unneeded before removal             | 5 minutes   |
    | Scale-down utilization threshold | Node utilization below which it is considered for removal   | 99%         |
    | Max bulk deletion of empty nodes | Maximum empty nodes deleted at once                         | 10          |
    | Max graceful termination time    | Time allowed for pods to terminate gracefully               | 600 seconds |

    <p>If the autoscaler cannot provision enough capacity, increase the maximum node count for the affected pool in [Node pools](/edge-ai/managed-kubernetes/manage-node-pools).</p>
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>The Cluster Autoscaler adds nodes when pods are pending and removes underutilized nodes. All autoscaler parameters are configured through a single PATCH request to the cluster — omitted parameters keep their current values, and an empty object restores Gcore defaults.</p>

    <Info>
      The Cluster Autoscaler scales based on CPU and memory utilization only. GPU utilization is not used as a scaling signal.
    </Info>

    <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}"
    export CLUSTER_NAME="{YOUR_CLUSTER_NAME}"
    ```

    ## Configure autoscaling

    <p>The `autoscaler_config` field is a flat key-value map where all values are strings, including numbers and booleans.</p>

    | Parameter                          | Type     | Description                                                                                      |
    | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
    | `scan-interval`                    | duration | How often the cluster is checked for scale-up or scale-down, e.g. `10s`                          |
    | `expander`                         | string   | Node group selection strategy: `random`, `most-pods`, `least-waste`, `price`, `priority`, `grpc` |
    | `scale-down-unneeded-time`         | duration | How long a node must be unneeded before removal, e.g. `10m`                                      |
    | `scale-down-utilization-threshold` | float    | Utilization ratio below which a node is considered for removal, e.g. `0.5`                       |
    | `scale-down-delay-after-add`       | duration | Wait after scale-up before scale-down evaluation resumes, e.g. `10m`                             |
    | `max-node-provision-time`          | duration | Maximum time to wait for a node to become ready, e.g. `15m`                                      |
    | `max-empty-bulk-delete`            | integer  | Maximum number of empty nodes deleted at once                                                    |
    | `max-graceful-termination-sec`     | integer  | Seconds allowed for pod termination before forced removal                                        |
    | `new-pod-scale-up-delay`           | duration | Pods newer than this value are excluded from scale-up triggering, e.g. `10s`                     |

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

        client = gcore.Gcore(api_key=os.environ["GCORE_API_KEY"])
        project_id = int(os.environ["GCORE_CLOUD_PROJECT_ID"])
        region_id = int(os.environ["GCORE_CLOUD_REGION_ID"])
        cluster_name = os.environ["CLUSTER_NAME"]

        result = client.cloud.k8s.clusters.update(
            cluster_name=cluster_name,
            project_id=project_id,
            region_id=region_id,
            autoscaler_config={
                "scan-interval": "20s",
                "scale-down-unneeded-time": "10m",
                "scale-down-utilization-threshold": "0.5",
                "expander": "least-waste",
            },
        )

        task_id = result.tasks[0]
        while True:
            task = client.cloud.tasks.get(task_id)
            if task.state in ("FINISHED", "ERROR"):
                break
            time.sleep(5)

        print("Autoscaling configured:", task.state)
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "os"
            "strconv"
            "time"

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

        func main() {
            projectID, _ := strconv.ParseInt(os.Getenv("GCORE_CLOUD_PROJECT_ID"), 10, 64)
            regionID, _ := strconv.ParseInt(os.Getenv("GCORE_CLOUD_REGION_ID"), 10, 64)
            clusterName := os.Getenv("CLUSTER_NAME")

            client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))
            ctx := context.TODO()

            result, err := client.Cloud.K8S.Clusters.Update(ctx, clusterName, cloud.K8SClusterUpdateParams{
                ProjectID: gcore.Int(projectID),
                RegionID:  gcore.Int(regionID),
                AutoscalerConfig: map[string]string{
                    "scan-interval":                    "20s",
                    "scale-down-unneeded-time":         "10m",
                    "scale-down-utilization-threshold": "0.5",
                    "expander":                         "least-waste",
                },
            })
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }

            taskID := result.Tasks[0]
            for {
                task, _ := client.Cloud.Tasks.Get(ctx, taskID)
                if task.State == "FINISHED" || task.State == "ERROR" {
                    fmt.Println("Autoscaling configured:", task.State)
                    break
                }
                time.Sleep(5 * time.Second)
            }
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X PATCH "https://api.gcore.com/cloud/v2/k8s/clusters/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$CLUSTER_NAME" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "autoscaler_config": {
              "scan-interval": "20s",
              "scale-down-unneeded-time": "10m",
              "scale-down-utilization-threshold": "0.5",
              "expander": "least-waste"
            }
          }'
        ```

        Response:

        ```json theme={null}
        {"tasks": ["facce763-b13a-4792-83ad-fefdcda7ae37"]}
        ```
      </Tab>
    </Tabs>

    <p>The API returns a task ID. Poll <code>GET /cloud/v1/tasks/{task_id}</code> every five seconds until `state` is `FINISHED`.</p>

    <p>To restore all autoscaler parameters to Gcore defaults, send an empty object:</p>

    ```bash theme={null}
    curl -X PATCH "https://api.gcore.com/cloud/v2/k8s/clusters/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$CLUSTER_NAME" \
      -H "Authorization: APIKey $GCORE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"autoscaler_config": {}}'
    ```
  </MethodSection>
</MethodSwitch>
