Cluster Autoscaling
Nodes are added and removed for you as your workloads grow and shrink. This
article explains how that works on AWS environments, what the node pools are,
how to pin a workload that must never be interrupted, and what to check when
scaling does not behave as you expect.
How autoscaling works
AWS environments use Karpenter for node autoscaling. No action is required
from your team for routine scaling.
When a pod cannot be scheduled because no node has enough available resources,
Karpenter:
- Selects an appropriate node type based on the pod's resource requests.
- Launches a new instance and registers it with the cluster.
- The pod is scheduled onto the new node.
When nodes are underutilised, Karpenter consolidates workloads onto fewer nodes
and terminates the excess.
Autoscaling decisions are driven entirely by your resource requests, so
accurate requests matter more here than anywhere else. Setting them is covered
in Workload Management.
Node pools
Each cluster has two default node pools.
| Node pool | Purpose | Instance type |
|---|---|---|
| main | General-purpose stateless workloads | SPOT instances |
| data | Stateful or data-intensive workloads | On-demand |
Your Components are scheduled onto the appropriate pool based on their
configuration. Node pool settings for your Environment are adjustable in SVIEW.
SPOT instances can be reclaimed by AWS at two minutes' notice. Stateless
workloads should be written to tolerate a restart at any time — handle SIGTERM
and shut down cleanly. Stateful workloads belong on the data pool.
Configuration — pinning workloads off SPOT nodes
Some workloads must not tolerate a SPOT interruption at all: a long-running
batch job that cannot resume from a partial state, or a migration that must run
to completion.
This requires a dedicated non-interruptible node pool provisioned for your
Environment. Such a pool is not created by default — if you do not have one and
you need one, raise a request and CTO2B will provision it and tell you the pool
name to use.
Once the pool exists, pin the deployment or pod to it with a matching node
affinity and toleration:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: pool-type
operator: In
values:
- uninterruptible-jobs
tolerations:
- key: "nodepool-type"
operator: "Equal"
value: "uninterruptible-jobs"
effect: "NoSchedule"Replace uninterruptible-jobs with the actual pool name configured for your
Environment. Both blocks are needed: the affinity sends the pod to that pool,
and the toleration lets it past the taint that keeps other workloads off it.
Troubleshooting scaling issues
| Symptom | Likely cause |
|---|---|
Pods stuck in Pending | The resource request cannot be satisfied by any node size — review requests and limits |
Pods stuck in Pending after pinning to a pool | The affinity value or toleration does not match the actual pool name, or the pool does not exist |
| Nodes not scaling down | Pods have no resource requests, or a disruption budget is preventing eviction |
| SPOT interruption causing downtime | The application is not shutting down gracefully — add SIGTERM handling, and run more than one replica |
If the problem persists after these checks, raise a request with the affected
namespace, the Component name, and the pod status you are seeing.
Related articles
Updated about 5 hours ago