URL has been copied successfully!
URL has been copied successfully!
URL has been copied successfully!
URL has been copied successfully!
URL has been copied successfully!
Share:
Twitter
LinkedIn
Facebook
Reddit
Follow by Email
Copy link
Threads
Bluesky
Reading Time: 7 minutes

I spotted this land in the aks-preview extension recently whilst keeping an eye on the AKS CLI pull requests. Azure/azure-cli-extensions#10448 adds --enable-upgrade-gate and --disable-upgrade-gate, and the description mentioned health-aware upgrades. That was enough for me to spin up a cluster and have a poke around.

The idea is simple. AKS checks health signals from monitoring components before it carries on with a node pool upgrade. If a provider says a node is unhealthy, or doesn’t answer in time, the upgrade stops instead of rolling through the rest of the pool.

There’s no announcement or how-to from the AKS team yet, and the docs link in the CLI help didn’t go anywhere useful when I tested. So I built a throwaway cluster, dug into what the feature installs and tried the healthy, unhealthy and no-response paths for real.

The short version is that the gate does stop bad upgrades. Getting a healthy upgrade through took a bit more work, thanks to a one-minute timeout in the built-in monitor.

If you want to know when the official docs land, keep an eye on my AKS Docs Tracker. Until then, this is what I found in the lab.

Why this matters

Upgrades are fine right up until they aren’t. New nodes come up, workloads move, and if something’s wrong you often find out after AKS has already carried on through the pool.

Upgrade gates give AKS a reason to stop. That’s a useful safety net, especially if you already have health checks you trust.

What the CLI change adds

The PR merged on 13 October 2026. I tested with Azure CLI 2.87.0 and aks-preview 22.0.0b15 in UK South.

The new flags land on four commands.

CommandUpgrade gate options
az aks create--enable-upgrade-gate
az aks update--enable-upgrade-gate and --disable-upgrade-gate
az aks nodepool add--enable-upgrade-gate and --disable-upgrade-gate
az aks nodepool update--enable-upgrade-gate and --disable-upgrade-gate

You can turn gating on for the whole cluster or opt in individual pools. Once it’s on at cluster level, a pool can’t opt out. Turning it off at cluster level doesn’t remove a pool’s own opt-in either, so check both if you’re tidying up.

The ARM API definition describes the signals as HealthSignal custom resources published by monitoring components in the cluster. It also says a force upgrade bypasses the gate. I didn’t use force upgrade at any point, as a successful upgrade with the gate bypassed wouldn’t tell us anything.

How to enable it

I used a standard AKS cluster with VM scale set pools rather than AKS Automatic, so I could control versions and upgrade order. It started with two Standard_D2s_v5 system nodes running Azure Linux 3, Azure CNI Overlay and Cilium.

I also turned on both Cluster Health Monitor options. I haven’t tested whether both are required, so treat that as my lab setup rather than a hard prerequisite.

First, update the extension and register the preview feature.

It can take a few minutes to show Registered. Once it does, refresh the resource provider registration.

That sorts out the subscription side. It doesn’t guarantee the feature works in every region or cluster configuration, so it’s worth checking it actually applied.

On an existing cluster, this update enables the gate and both monitors.

You can pass the same flags to az aks create for a new cluster. Either way, check the result rather than assuming it worked.

In my lab, the gate came back as enabled: true and both monitor settings were true. I also disabled automatic Kubernetes and node OS upgrades so nothing kicked off an upgrade behind my back.

What it installs in the cluster

The unit tests in the PR cover flag validation and the API property, but they don’t show what a real HealthSignal looks like. The cluster was much more helpful. With kubectl connected, I went looking for new custom resource definitions (CRDs).

Alongside CheckNodeHealth from the on-demand monitor, four new resources stood out.

ResourceWhat its installed schema describes
UpgradeGatePolicyRegisters a monitoring provider and the cluster or pool it gates
UpgradeOperationRepresents an upgrade operation created by the AKS Resource Provider
HealthCheckRequestRequests monitoring for a node, node pool or cluster
HealthSignalCarries the monitoring provider’s health verdict

They’re all cluster-scoped. The health resources use health.aks.io/v1alpha1, and the upgrade ones use upgrade.aks.io/v1alpha1. You can see them in the Azure portal too, under Kubernetes resources > Custom resources, if you filter on those two groups.

Azure portal Custom resources view showing the HealthCheckRequest, HealthSignal, UpgradeGatePolicy and UpgradeOperation CRDs, all cluster-scoped

The schemas are the closest thing to documentation right now, so kubectl explain is your friend.

They spell out how the pieces fit together. A monitoring provider watches for a HealthCheckRequest, creates a HealthSignal that points back to it with an owner reference, then writes its verdict to the signal’s status.

The bit that’s easy to miss is the provider label. A custom provider’s signal needs a health.aks.io/provider label that matches the provider in its policy. Creating any old signal won’t do anything.

The schema also explains how verdicts are treated. For a node, True lets the upgrade carry on, False stops it, and Unknown or no answer waits until the provider’s deadline and then stops it. At pool and cluster level, Unknown doesn’t hold things up, but an explicit False still can.

I tested the node-level unhealthy and no-answer cases below. The rest comes straight from the schema.

Testing the gate with a fake provider

I didn’t want to break DNS or networking just to make a node look unhealthy. Instead, I wrote a small synthetic provider that could answer healthy, unhealthy or not at all.

It doesn’t measure anything real, so a synthetic True says nothing about your workloads. It just let me check whether AKS actually acts on a provider’s verdict.

This is the policy I used for the later user pool tests.

It registers my provider for the healthtest pool and gives it 90 seconds to respond. The policy doesn’t install anything, though. Apply it without something answering requests and your upgrade will time out.

My responder ran locally against an isolated lab kubeconfig. It watched for requests, created owner-linked signals with its provider label and wrote a Ready condition to the status. In unhealthy mode it returned False for nodes. In silent mode it didn’t create a signal at all.

Whilst an upgrade runs, these commands show what’s going on.

Requests and signals appear during the rollout and get cleaned up afterwards. So an empty list before an upgrade doesn’t tell you much.

An unhealthy node stopped the upgrade

For the first test, I kicked off a real pool upgrade to Kubernetes 1.37.1 and had the provider return Ready=False for the surge node. The operation failed with UpgradeAbortAtUpgradeGate, and the error included this.

I liked that AKS quoted my condition and reason back. This wasn’t a random upgrade failure that happened to line up with the test. The gate acted on the verdict.

Keep in mind that an abort isn’t a rollback. It stops progress, but it doesn’t promise to undo nodes that have already been upgraded.

No answer stopped it too

Next, I set the provider to stay silent and tried again. AKS failed the upgrade, this time naming my provider and its deadline.

So the 90-second nodeTimeout works. It’s how long AKS waits for that provider’s verdict on a node, not a limit on the whole upgrade.

What caught me out

My first healthy attempt failed, even though my provider was happily returning True.

AKS aborted on a reimaged node because the built-in ClusterHealthMonitor hadn’t reported healthy within one minute. A retry hit exactly the same thing.

That corrected an assumption I’d made. There weren’t any policies or signals in the cluster before my first upgrade, but the built-in monitor still took part. Don’t assume gating is inactive just because those lists are empty.

To rule out the system pool, I added a separate one-node user pool and upgraded it from 1.37.0 to 1.37.1. The system nodes and monitoring deployment were left alone, the baseline node checks passed and the monitor stayed on its original node throughout.

It still hit the one-minute timeout.

The timings told the story. The request for the reimaged node appeared at about 10:10:17 UTC. Its boot health check started at 10:10:55, and the checker container didn’t start until 10:11:27. That’s already past the one-minute window.

The checks did report healthy afterwards, but by then the upgrade had failed. It looks like a timing issue between the gate and on-demand health checking, although I can’t confirm the root cause from the outside.

Trying a longer timeout

The policy schema has a nodeTimeout field, capped at five minutes, but there’s no matching timeout in the ARM gate settings. So I tried a policy naming the built-in ClusterHealthMonitor provider, scoped to the test pool only. Treat this as an experiment, not a proven fix.

Kubernetes accepted it without complaint. I recreated the test pool on 1.37.0, waited for its baseline health checks to pass and ran another real upgrade.

This time it returned Succeeded. The pool reached 1.37.1, the gate was still enabled, both monitors were still on and there was no force upgrade involved.

Here’s the portal afterwards, with both pools running and their expected node counts.

Azure portal node pool list showing healthtest and systempool in Succeeded state, with one and two Ready nodes

That shows where the lab ended up, not the upgrade history.

Now the honest bit. The native node checks finished within 60 seconds on this run. So the healthy upgrade worked with the five-minute policy in place, but I can’t say the policy changed the built-in deadline or caused the success. To prove that, I’d need a timeout error showing the new value, and I haven’t got that yet.

Wrapping up

Health-aware upgrade gates look like a really useful addition to AKS. My fake provider stopped an upgrade with an unhealthy answer and with no answer at all, and a real healthy patch upgrade got through in the end.

Here’s where the lab landed.

TestObserved result
Custom node signal reports unhealthyUpgrade aborted and cited SyntheticUnhealthy
Custom provider returns no verdictUpgrade aborted with the configured 90-second timeout
Healthy responses with the original setupBuilt-in monitor timed out on reimaged nodes
Separate user pool with the original timeoutSame built-in timeout, without moving the monitoring deployment
Healthy user-pool upgrade with the experimental policy presentReal 1.37.0 to 1.37.1 rollout succeeded
Five-minute override for the built-in providerNot proven

A same-version reconcile also got a failed pool back to Succeeded along the way. I didn’t count that as a healthy test, as it wasn’t a fresh gated rollout.

If you’re going to try it, use a throwaway cluster and keep a close eye on the built-in monitor timings. That one-minute window is what caught me out. Also, don’t apply a custom provider policy until something’s actually answering it, or you’ll end up blocking your own upgrades.

I’d like to see the official docs cover provider registration, the built-in monitor deadlines and how to recover after an aborted upgrade. A supported way to tune that built-in timeout would be great too.

Until then, the CLI PR, API definitions and installed CRDs are the best places to dig in. Keep an eye on the AKS Docs Tracker for the official docs when they land.

If you try it, let me know how it goes.

Share:
Twitter
LinkedIn
Facebook
Reddit
Follow by Email
Copy link
Threads
Bluesky

Pixel Robots.

I’m Richard Hooper aka Pixel Robots. I started this blog in 2016 for a couple reasons. The first reason was basically just a place for me to store my step by step guides, troubleshooting guides and just plain ideas about being a sysadmin. The second reason was to share what I have learned and found out with other people like me. Hopefully, you can find something useful on the site.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *