Node-pool model
This page covers the node-pool model in depth. For step-by-step instructions on creating and managing pools, see the Node pools guide.
Identical nodes per pool
Section titled “Identical nodes per pool”A node pool is a set of identical worker nodes. Every node in a pool has the same size, so your workloads see uniform capacity wherever they’re scheduled within the pool. To run different node sizes in one cluster, you create multiple pools.
Exact sizing
Section titled “Exact sizing”There are no machine tiers. You specify the exact resources for each node in a pool:
- vCPUs
- Memory (GB)
- Disk (GB)
This lets you size nodes to your actual workload rather than rounding up to the nearest predefined tier.
Fixed vs autoscaling
Section titled “Fixed vs autoscaling”The per-node size is always fixed for a pool. What a pool’s kind controls is how many of those nodes run — and the kind is decided by the pool’s CPU/memory budget, never by a node count:
- Fixed pool — the budget permits no scaling: minimum and maximum total vCPUs are equal, and so are minimum and maximum total memory (GB). The pool holds a constant number of nodes. Predictable capacity and predictable cost.
- Autoscaling pool — the budget’s maximum exceeds its minimum on either dimension. Aether’s autoscaler adds nodes — each one the pool’s fixed per-node size — when pods can’t be scheduled, and removes them when capacity is no longer needed, keeping the pool’s totals within the budget. The node-count bounds are derived from the budget.
A pool’s node count therefore never decides its kind: an autoscaling pool can sit at a single node, and that does not make it a fixed one.
The kind also determines billing, and every node’s 15-minute grace period
comes first: a node removed inside it never adds a charge. Every node has one,
measured from the moment it
first appears in the cluster’s node list, in either kind of pool and whether or
not it becomes Ready — the pool’s first nodes, nodes added later, and
replacement nodes alike. A node removed within that window carries no node
charge.
Beyond that the pool’s kind applies. A fixed pool bills per pool rather than per node, so its grace period works through the pool: its flat monthly fee is not charged if the pool is removed within 15 minutes of its first node appearing in the cluster’s node list, and once the pool is past its grace period that calendar month is locked — full month, no proration, not refunded if you delete the pool. An autoscaling pool bills per started node-hour, per node, counted from the moment each node first appears in the cluster’s node list — the same event the grace period starts from — over the node’s whole lifetime from that moment; the grace period is not deducted. See the Billing model.
Removal, draining and billing
Section titled “Removal, draining and billing”These three run on different clocks, and it is worth separating them:
- Billing stops when removal is requested — when you delete a node, scale the pool down, or delete the pool or the cluster. That instant, not the moment the node finally disappears, is the cutoff.
- The drain happens after that instant and is never billed. Scale a pool down at minute 12 and let the node drain until minute 21, and its lifetime for billing is 12 minutes — inside the grace period, so no charge. A long drain timeout costs you nothing.
- The anti-abuse deferral is not a drain. Where an account creates and destroys nodes in a pattern that exploits the grace period, Aether may defer a removal past the window and charge for the node. That is an exception applied to abuse, not the ordinary course, and it moves the billing cutoff — which a drain never does.
Drain timeout
Section titled “Drain timeout”When a pool shrinks — you scale it down, the autoscaler removes surplus capacity, or you delete the pool — Aether drains each node before it goes away: the node is cordoned and its pods are evicted so they can be rescheduled elsewhere. The drain timeout is how long the platform waits for that to finish before removing the node anyway.
- It’s set per pool, in seconds, anywhere from 30 to 3600 (30 seconds to 1 hour).
- Leave it unset and the pool uses the platform default of 600 seconds (10 minutes). An unset pool keeps tracking that default rather than being pinned to today’s value.
- Unlike labels and taints, it can be changed at any time on an existing pool.
The timeout is a budget, not a guarantee. Inside it your
PodDisruptionBudgets are honoured and your pods get their
terminationGracePeriodSeconds to shut down cleanly. Once it expires the node
is removed regardless, and anything still running on it is terminated.
How to pick a value:
- Raise it if your workloads need a long, orderly shutdown — draining
connections, flushing to disk, finishing in-flight jobs — or if strict
PodDisruptionBudgets mean pods can only move a few at a time. - Lower it if your workloads are stateless and you’d rather scale down quickly.
Scale-down decision settings
Section titled “Scale-down decision settings”The drain timeout above governs the removal of a node once the platform has decided the node should go. Two further per-pool settings govern the decision itself — when the autoscaler concludes a node is surplus in the first place. They apply to autoscaling pools; a fixed pool has no autoscaler node group, so on a fixed pool the values are stored but inert.
| Setting | Range | Platform default | What it decides |
|---|---|---|---|
| Scale-down delay | 60–3600 seconds | 600 s (10 minutes) | How long a node must remain underutilised before the autoscaler removes it. |
| Scale-down utilisation threshold | 10–90 % | 50 % | A node counts as underutilised when both its CPU and its memory requests are below this share of the node’s allocatable capacity. |
Both are evaluated against pod resource requests, not observed usage — a pod that requests far more than it consumes keeps its node above the threshold.
- Unset means the platform default, and keeps tracking it. A pool that has never had a value set follows the platform default if that default changes; it is not pinned to today’s number.
- A value can be changed at any time on an existing pool, but it cannot be cleared back to the default: submitting the field empty is read as “leave unchanged”. Setting it explicitly back to 600 s / 50 % restores default behaviour without restoring the tracking.
- Changing either setting takes effect without replacing any node — it retunes the running autoscaler for that pool.
Underneath, Aether projects the resolved values onto the pool’s
MachineDeployment as the Cluster API autoscaler’s own options annotations:
cluster.x-k8s.io/autoscaling-options-scaledownunneededtime— the delay as a duration, e.g.600s.cluster.x-k8s.io/autoscaling-options-scaledownutilizationthreshold— the threshold as a fraction, e.g.0.5for 50 %.
They are always written, with the platform default when the pool has no value of its own, so the pool’s behaviour is always explicit at the MachineDeployment rather than inherited from the autoscaler’s process-wide flags.
The two settings compose with the drain timeout in sequence: delay and threshold decide that a node goes, then the drain timeout bounds how long draining it may take before it goes anyway.
To exempt a particular workload or node from scale-down entirely, use the
autoscaler’s native annotations —
cluster-autoscaler.kubernetes.io/safe-to-evict: "false" on a pod, or
cluster-autoscaler.kubernetes.io/scale-down-disabled: "true" on a node. See
Node pools.
Node labels and taints
Section titled “Node labels and taints”A pool can carry Kubernetes labels and taints, set when it’s created:
- Labels are
key=valuepairs; taints arekey=value:Effect, whereEffectisNoSchedule,PreferNoSchedule, orNoExecute. Either may be empty. - Aether applies them to the pool’s nodes automatically at provisioning — there’s
no manual
kubectl labelorkubectl taintstep. - A taint repels pods that don’t tolerate it. To target a tainted or labeled
pool, add a matching
nodeSelectorandtolerationto your own pods; Aether doesn’t modify tenant workloads. Aether’s platform components already tolerate the taints, so a tainted node still runs the cluster’s own services.
Two guardrails apply:
- Reserved prefixes are rejected. Keys beginning with
aether.cloud/,kubernetes.io/,k8s.io/,node-role.kubernetes.io/, ornode.kubernetes.io/can’t be used. - At least one schedulable pool must remain. A cluster must always keep one
pool with no
NoScheduleorNoExecutetaint so platform services have somewhere to run; Aether rejects a change that would leave none.
Labels and taints are immutable after create — to re-tier, create a new pool with the labels/taints you want and drain the old one.
Changing a fixed pool
Section titled “Changing a fixed pool”What you can change on an existing fixed pool, and what each change costs:
- The node count. Raising it adds nodes; lowering it drains and removes them. Lowering it mid-month does not reduce that month’s locked amount — the next month locks at the new size. Raising it is reflected from next month’s amount on a custom cluster, with no extra charge inside the month you make the change. On a package cluster it can cost you that month: growth that takes the cluster past its allowance for the first time — counting only nodes whose grace period has expired — creates an overage line, and a line that first appears mid-month is charged for that month. An overage line that already exists is not repriced until next month.
- The per-node vCPU, memory or disk. Changing a fixed pool’s per-node vCPU, memory or disk after creation is not supported today. To change node size, create a new pool with the new shape and drain the old one.
- Labels and taints. Immutable, as above — same remedy: a new pool.
Deleting a pool and creating another — even with the same name, in the same month — is not an edit. It creates a new pool, with its own 15-minute grace period and its own monthly charge, and the old pool’s locked month stays payable.
Lifecycle states
Section titled “Lifecycle states”The portal surfaces each pool’s current state so you always know what’s happening:
- Provisioning — the pool’s nodes are being created and joined to the cluster.
- Scaling — nodes are being added (e.g. after you raise the count, or when an autoscaling pool grows).
- Scaling down — nodes are being drained and removed (e.g. after you lower the count, or when an autoscaling pool releases surplus capacity). Billing for those nodes already stopped when the removal was requested; this state covers the drain.
- Running — the pool is at its target size and healthy.
- Degraded — one or more nodes are unhealthy or not at the expected state; Aether works to reconcile it.
- Deleting — the pool is being torn down and its nodes removed.
These states let you follow scaling and provisioning operations in real time and confirm a pool is healthy before relying on it.
Learn more
Section titled “Learn more”- Node pools — add, scale, and delete pools.
- Billing model — how each pool kind is charged.