<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>AKS Engineering Blog</title>
        <link>https://blog.aks.azure.com</link>
        <description>AKS Engineering Blog Blog</description>
        <lastBuildDate>Mon, 29 Jun 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Routing agent traffic is really three decisions]]></title>
            <link>https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks</link>
            <guid>https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks</guid>
            <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Agents make many LLM calls in loops, so routing each to the right model — cheap self-hosted or frontier — keeps them affordable. Three decisions on AKS.]]></description>
            <content:encoded><![CDATA[<p>A chat turn is one LLM call. An agent is hundreds — a reasoning loop that plans, calls a tool, reads the result, re-plans, and only sometimes stops. The cost and latency you signed up for in a demo get multiplied by that loop, and most of those calls are <em>easy</em> — a tool-argument fill, a yes/no gate, a short summary — that never needed a frontier model. So the question isn't "which model is best." It's "which model should answer <em>this specific call</em>, and how do I govern a flood of them?" That's not one decision; it's three, on different signals.</p>
<p>This post wires up those three decisions on AKS as a single OpenAI-compatible endpoint: a trivial agent step lands on a cheap self-hosted model (KAITO + vLLM, placed by GPU state) while a hard step escalates to a frontier model on Azure OpenAI — every call authenticated, metered, and traced. We lean on managed Azure throughout (<strong>KAITO</strong> for serving, <strong>Azure OpenAI</strong> for the frontier path, <strong>Azure Managed Prometheus and Grafana</strong> for observability); the two layers without a drop-in managed service — the semantic router and the AI gateway — run as open source on the cluster.</p>
<p><img decoding="async" loading="lazy" alt="Title card: &amp;quot;Routing agent traffic is really three decisions.&amp;quot; Three chips read content, then policy, then GPU state — the through-line of the post — over the stack RouteLLM, agentgateway, Gateway API Inference Extension, and KAITO + vLLM on AKS." src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTIwMCA0ODAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcm9sZT0iaW1nIiBhcmlhLWxhYmVsPSJUaGUgdGhyZWUgcm91dGluZyBkZWNpc2lvbnMgZm9yIGFnZW50IHRyYWZmaWM6IGNvbnRlbnQsIHRoZW4gcG9saWN5LCB0aGVuIEdQVSBzdGF0ZSwgb3ZlciB0aGUgc3RhY2sgUm91dGVMTE0sIGFnZW50Z2F0ZXdheSwgR2F0ZXdheSBBUEkgSW5mZXJlbmNlIEV4dGVuc2lvbiwgYW5kIEtBSVRPIHdpdGggdkxMTSBvbiBBS1MuIj4KICA8ZGVmcz4KICAgIDxtYXJrZXIgaWQ9Imh4IiBtYXJrZXJXaWR0aD0iMTAiIG1hcmtlckhlaWdodD0iMTAiIHJlZlg9IjYuNSIgcmVmWT0iMyIgb3JpZW50PSJhdXRvIiBtYXJrZXJVbml0cz0ic3Ryb2tlV2lkdGgiPgogICAgICA8cGF0aCBkPSJNMCwwIEw2LDMgTDAsNiB6IiBmaWxsPSIjOUZCNENDIi8+CiAgICA8L21hcmtlcj4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iYmciIHgxPSIwIiB5MT0iMCIgeDI9IjEiIHkyPSIxIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMEIxRjNBIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iMC41NSIgc3RvcC1jb2xvcj0iIzBFMkM1MiIvPgogICAgICA8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMxMDM5NkIiLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9ImNoaXAiIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjRkZGRkZGIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0VBRjJGQiIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxzdHlsZT4KICAgICAgdGV4dCB7IGZvbnQtZmFtaWx5OiAiU2Vnb2UgVUkiLCAtYXBwbGUtc3lzdGVtLCBzeXN0ZW0tdWksICJIZWx2ZXRpY2EgTmV1ZSIsIHNhbnMtc2VyaWY7IH0KICAgICAgLmV5ZWJyb3cgeyBmaWxsOiM3RkIwRTY7IGZvbnQtd2VpZ2h0OjYwMDsgbGV0dGVyLXNwYWNpbmc6M3B4OyB9CiAgICAgIC5jdCAgeyBmaWxsOiMyNDNBNUU7IGZvbnQtd2VpZ2h0OjcwMDsgfQogICAgICAuY3MgIHsgZmlsbDojNUI2QjdCOyBmb250LXdlaWdodDo1MDA7IH0KICAgICAgLm51bSB7IGZpbGw6IzAwNzhENDsgZm9udC13ZWlnaHQ6NzAwOyB9CiAgICAgIC5zaWcgeyBmaWxsOiM0RkMzRjc7IGZvbnQtd2VpZ2h0OjYwMDsgfQogICAgPC9zdHlsZT4KICA8L2RlZnM+CgogIDwhLS0gQmFja2Ryb3AgLS0+CiAgPHJlY3Qgd2lkdGg9IjEyMDAiIGhlaWdodD0iNDgwIiBmaWxsPSJ1cmwoI2JnKSIvPgogIDwhLS0gc3VidGxlIEdQVS1ncmlkIHRleHR1cmUsIHJpZ2h0IC0tPgogIDxnIG9wYWNpdHk9IjAuMTAiIHN0cm9rZT0iIzlGRDBGRiIgc3Ryb2tlLXdpZHRoPSIxLjQiPgogICAgPGxpbmUgeDE9Ijk4MCIgeTE9Ii00MCIgeDI9IjEzMDAiIHkyPSIyODAiLz4KICAgIDxsaW5lIHgxPSIxMDUwIiB5MT0iLTQwIiB4Mj0iMTM3MCIgeTI9IjI4MCIvPgogICAgPGxpbmUgeDE9IjExMjAiIHkxPSItNDAiIHgyPSIxNDQwIiB5Mj0iMjgwIi8+CiAgICA8bGluZSB4MT0iOTAwIiB5MT0iNDAiICB4Mj0iMTMwMCIgeTI9IjQwIi8+CiAgICA8bGluZSB4MT0iOTAwIiB5MT0iMTAwIiB4Mj0iMTMyMCIgeTI9IjEwMCIvPgogIDwvZz4KCiAgPCEtLSBFeWVicm93ICsgdGhyb3VnaC1saW5lIGNhcHRpb24gLS0+CiAgPHRleHQgeD0iNjAwIiB5PSI3OCIgZm9udC1zaXplPSIyMCIgY2xhc3M9ImV5ZWJyb3ciIHRleHQtYW5jaG9yPSJtaWRkbGUiPkFHRU5UIFRSQUZGSUMgT04gQUtTPC90ZXh0PgogIDx0ZXh0IHg9IjYwMCIgeT0iMTI2IiBmb250LXNpemU9IjMwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjRkZGRkZGIiBmb250LXdlaWdodD0iNzAwIj5Db250ZW50IDx0c3BhbiBmaWxsPSIjNUU3QkEwIj4mIzg1OTQ7PC90c3Bhbj4gUG9saWN5IDx0c3BhbiBmaWxsPSIjNUU3QkEwIj4mIzg1OTQ7PC90c3Bhbj4gR1BVIHN0YXRlPC90ZXh0PgoKICA8IS0tIFRocmVlIGNoaXBzIC0tPgogIDwhLS0gY2hpcCAxIC0tPgogIDxnPgogICAgPHJlY3QgeD0iODAiIHk9IjE4NiIgd2lkdGg9IjMyMCIgaGVpZ2h0PSIxNTAiIHJ4PSIxOCIgZmlsbD0idXJsKCNjaGlwKSIgc3Ryb2tlPSIjQ0ZFMEYyIiBzdHJva2Utd2lkdGg9IjEuNSIvPgogICAgPGNpcmNsZSBjeD0iMTIwIiBjeT0iMjI0IiByPSIxNyIgZmlsbD0iI0U2RjFGQyIvPgogICAgPHRleHQgeD0iMTIwIiB5PSIyMzAiIGZvbnQtc2l6ZT0iMTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGNsYXNzPSJudW0iPjE8L3RleHQ+CiAgICA8dGV4dCB4PSIxNTIiIHk9IjIzMCIgZm9udC1zaXplPSIyMiIgY2xhc3M9ImN0Ij5Db250ZW50PC90ZXh0PgogICAgPHRleHQgeD0iMTA2IiB5PSIyNzAiIGZvbnQtc2l6ZT0iMTUiIGNsYXNzPSJjcyI+V2hpY2ggbW9kZWwgc2hvdWxkPC90ZXh0PgogICAgPHRleHQgeD0iMTA2IiB5PSIyOTIiIGZvbnQtc2l6ZT0iMTUiIGNsYXNzPSJjcyI+YW5zd2VyIHRoaXMgY2FsbD88L3RleHQ+CiAgICA8dGV4dCB4PSIxMDYiIHk9IjMxOCIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzAwNzhENCIgZm9udC13ZWlnaHQ9IjYwMCI+c2VtYW50aWMgcm91dGVyPC90ZXh0PgogIDwvZz4KICA8bGluZSB4MT0iNDEyIiB5MT0iMjYxIiB4Mj0iNDU2IiB5Mj0iMjYxIiBzdHJva2U9IiM5RkI0Q0MiIHN0cm9rZS13aWR0aD0iMi40IiBtYXJrZXItZW5kPSJ1cmwoI2h4KSIvPgogIDwhLS0gY2hpcCAyIC0tPgogIDxnPgogICAgPHJlY3QgeD0iNDcwIiB5PSIxODYiIHdpZHRoPSIzMjAiIGhlaWdodD0iMTUwIiByeD0iMTgiIGZpbGw9InVybCgjY2hpcCkiIHN0cm9rZT0iI0NGRTBGMiIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICAgIDxjaXJjbGUgY3g9IjUxMCIgY3k9IjIyNCIgcj0iMTciIGZpbGw9IiNFNkYxRkMiLz4KICAgIDx0ZXh0IHg9IjUxMCIgeT0iMjMwIiBmb250LXNpemU9IjE4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBjbGFzcz0ibnVtIj4yPC90ZXh0PgogICAgPHRleHQgeD0iNTQyIiB5PSIyMzAiIGZvbnQtc2l6ZT0iMjIiIGNsYXNzPSJjdCI+UG9saWN5PC90ZXh0PgogICAgPHRleHQgeD0iNDk2IiB5PSIyNzAiIGZvbnQtc2l6ZT0iMTUiIGNsYXNzPSJjcyI+QXV0aCwgYnVkZ2V0cywgbGltaXRzLDwvdGV4dD4KICAgIDx0ZXh0IHg9IjQ5NiIgeT0iMjkyIiBmb250LXNpemU9IjE1IiBjbGFzcz0iY3MiPmd1YXJkcmFpbHMsIHRyYWNpbmcuPC90ZXh0PgogICAgPHRleHQgeD0iNDk2IiB5PSIzMTgiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMwMDc4RDQiIGZvbnQtd2VpZ2h0PSI2MDAiPkFJIGdhdGV3YXk8L3RleHQ+CiAgPC9nPgogIDxsaW5lIHgxPSI4MDIiIHkxPSIyNjEiIHgyPSI4NDYiIHkyPSIyNjEiIHN0cm9rZT0iIzlGQjRDQyIgc3Ryb2tlLXdpZHRoPSIyLjQiIG1hcmtlci1lbmQ9InVybCgjaHgpIi8+CiAgPCEtLSBjaGlwIDMgLS0+CiAgPGc+CiAgICA8cmVjdCB4PSI4NjAiIHk9IjE4NiIgd2lkdGg9IjMyMCIgaGVpZ2h0PSIxNTAiIHJ4PSIxOCIgZmlsbD0idXJsKCNjaGlwKSIgc3Ryb2tlPSIjQ0ZFMEYyIiBzdHJva2Utd2lkdGg9IjEuNSIvPgogICAgPGNpcmNsZSBjeD0iOTAwIiBjeT0iMjI0IiByPSIxNyIgZmlsbD0iI0U2RjFGQyIvPgogICAgPHRleHQgeD0iOTAwIiB5PSIyMzAiIGZvbnQtc2l6ZT0iMTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGNsYXNzPSJudW0iPjM8L3RleHQ+CiAgICA8dGV4dCB4PSI5MzIiIHk9IjIzMCIgZm9udC1zaXplPSIyMiIgY2xhc3M9ImN0Ij5HUFUgc3RhdGU8L3RleHQ+CiAgICA8dGV4dCB4PSI4ODYiIHk9IjI3MCIgZm9udC1zaXplPSIxNSIgY2xhc3M9ImNzIj5XaGljaCByZXBsaWNhIHRha2VzIGl0LDwvdGV4dD4KICAgIDx0ZXh0IHg9Ijg4NiIgeT0iMjkyIiBmb250LXNpemU9IjE1IiBjbGFzcz0iY3MiPmJ5IGxpdmUgbG9hZD88L3RleHQ+CiAgICA8dGV4dCB4PSI4ODYiIHk9IjMxOCIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzAwNzhENCIgZm9udC13ZWlnaHQ9IjYwMCI+aW5mZXJlbmNlIExCPC90ZXh0PgogIDwvZz4KCiAgPCEtLSBmb290ZXIgd29yZG1hcmsgLS0+CiAgPHRleHQgeD0iNjAwIiB5PSI0MDQiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiM3RTk5QkIiIGZvbnQtd2VpZ2h0PSI1MDAiIHRleHQtYW5jaG9yPSJtaWRkbGUiPlJvdXRlTExNICYjMTgzOyBhZ2VudGdhdGV3YXkgJiMxODM7IEdhdGV3YXkgQVBJIEluZmVyZW5jZSBFeHRlbnNpb24gJiMxODM7IEtBSVRPICsgdkxMTTwvdGV4dD4KPC9zdmc+Cg==" width="1200" height="480" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-three-decisions">The three decisions<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#the-three-decisions" class="hash-link" aria-label="Direct link to The three decisions" title="Direct link to The three decisions" translate="no">​</a></h2>
<p>"Route this request" hides three separate questions, each reading a different signal, each with a different owner. For an agent firing calls in a loop, the first two fire on every call and the third on every weak-path call:</p>
<ol>
<li><strong>Which model should answer this call?</strong> A multi-step reasoning hop deserves a frontier model; "extract the date from this string" does not. Answering means reading the request <em>content</em> and predicting answer quality. This is <strong>semantic routing</strong>, and we'll use <strong><a href="https://github.com/lm-sys/RouteLLM" target="_blank" rel="noopener noreferrer">RouteLLM</a></strong>.</li>
<li><strong>How do I expose, secure, govern, and forward this traffic?</strong> Provider abstraction, auth, per-agent budgets, rate limits, guardrails, observability. None of that cares about prompt meaning — it's a <em>policy</em> job for a data plane. We'll use <strong><a href="https://agentgateway.dev/docs/" target="_blank" rel="noopener noreferrer">agentgateway</a></strong>, an OpenAI-compatible, agent-native proxy, in the cluster.</li>
<li><strong>Which replica of the chosen model gets this call?</strong> Across a fleet of GPU pods the right target depends on KV-cache occupancy, queue depth, and which LoRA adapters are already resident — live <em>GPU state</em>. That's the <strong><a href="https://gateway-api-inference-extension.sigs.k8s.io/" target="_blank" rel="noopener noreferrer">Gateway API Inference Extension</a></strong> and its Endpoint Picker.</li>
</ol>
<p><strong>Content → policy → GPU state.</strong> That's the through-line, and the discipline is refusing to let any layer answer a question that belongs to another: the router never thinks about auth, the gateway never thinks about prompt difficulty, the Endpoint Picker never thinks about <em>which</em> model — only <em>which replica</em>.</p>
<p><img decoding="async" loading="lazy" alt="Structure and ownership of the three-layer agent-traffic routing design on AKS: RouteLLM (Layer 1) and agentgateway (Layer 2) run as open source inside the AKS cluster; agentgateway calls the Inference Extension Endpoint Picker directly and forwards to KAITO-served vLLM pods (Layer 3). The gateway abstracts a managed Azure OpenAI frontier backend and the self-hosted backend, and Azure Monitor scrapes vLLM and cost metrics. Solid lines show composition and ownership, not request order." src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgOTQwIDg1MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxtYXJrZXIgaWQ9ImFyIiBtYXJrZXJXaWR0aD0iOSIgbWFya2VySGVpZ2h0PSI5IiByZWZYPSI2IiByZWZZPSIzIiBvcmllbnQ9ImF1dG8iIG1hcmtlclVuaXRzPSJzdHJva2VXaWR0aCI+CiAgICAgIDxwYXRoIGQ9Ik0wLDAgTDYsMyBMMCw2IHoiIGZpbGw9IiM1QjZCN0IiLz4KICAgIDwvbWFya2VyPgogICAgPHN0eWxlPgogICAgICB0ZXh0IHsgZm9udC1mYW1pbHk6ICJTZWdvZSBVSSIsIC1hcHBsZS1zeXN0ZW0sIHN5c3RlbS11aSwgIkhlbHZldGljYSBOZXVlIiwgc2Fucy1zZXJpZjsgfQogICAgICAudCB7IGZpbGw6IzI0M0E1RTsgZm9udC13ZWlnaHQ6NjAwOyB9CiAgICAgIC5zIHsgZmlsbDojNUI2QjdCOyB9CiAgICAgIC5yZWwgeyBmaWxsOiM4QTk4QTY7IGZvbnQtc3R5bGU6aXRhbGljOyB9CiAgICA8L3N0eWxlPgogIDwvZGVmcz4KCiAgPHJlY3QgeD0iMiIgeT0iMiIgd2lkdGg9IjkzNiIgaGVpZ2h0PSI4NDYiIHJ4PSIxNCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjRTJFOEYwIiBzdHJva2Utd2lkdGg9IjEuNSIvPgoKICA8IS0tIFRpdGxlIC0tPgogIDx0ZXh0IHg9IjM2IiB5PSI0MCIgZm9udC1zaXplPSIyMSIgY2xhc3M9InQiPlJvdXRpbmcgYWdlbnQgdHJhZmZpYyBvbiBBS1Mgwrcgc3RydWN0dXJlICZhbXA7IG93bmVyc2hpcDwvdGV4dD4KICA8dGV4dCB4PSIzNiIgeT0iNjIiIGZvbnQtc2l6ZT0iMTIiIGNsYXNzPSJzIj5ob3cgdGhlIGxheWVycyBjb21wb3NlIGFuZCB3aG8gb3ducyBlYWNoIOKAlCBtYW5hZ2VkIEF6dXJlIHZzLiBvcGVuIHNvdXJjZSBvbiBBS1M8L3RleHQ+CgogIDwhLS0gTGVnZW5kIC0tPgogIDxyZWN0IHg9IjY0OCIgeT0iMjgiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgcng9IjMiIGZpbGw9IiMwMDc4RDQiLz4KICA8dGV4dCB4PSI2NzAiIHk9IjQxIiBmb250LXNpemU9IjExIiBjbGFzcz0idCIgZm9udC13ZWlnaHQ9IjQwMCI+TWFuYWdlZCBBenVyZTwvdGV4dD4KICA8cmVjdCB4PSI2NDgiIHk9IjUwIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiM2NDc0OEIiIHN0cm9rZS13aWR0aD0iMS41Ii8+CiAgPHRleHQgeD0iNjcwIiB5PSI2MyIgZm9udC1zaXplPSIxMSIgY2xhc3M9InQiIGZvbnQtd2VpZ2h0PSI0MDAiPk9wZW4gc291cmNlIG9uIEFLUzwvdGV4dD4KCiAgPCEtLSBDbGllbnQgLS0+CiAgPHJlY3QgeD0iMzQwIiB5PSI4NCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSI0NiIgcng9IjEwIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiNDOUQ2RTIiIHN0cm9rZS13aWR0aD0iMS4zIi8+CiAgPHJlY3QgeD0iMzUyIiB5PSI5NyIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE4IiByeD0iNCIgZmlsbD0iIzY0NzQ4QiIvPgogIDx0ZXh0IHg9IjM3OCIgeT0iMTA1IiBmb250LXNpemU9IjEzIiBjbGFzcz0idCI+QWdlbnQgLyBhcHA8L3RleHQ+CiAgPHRleHQgeD0iMzc4IiB5PSIxMjEiIGZvbnQtc2l6ZT0iMTAiIGNsYXNzPSJzIj5tYW55IGNhbGxzIHBlciB0YXNrIMK3IGxvb3AgJmFtcDsgdG9vbHM8L3RleHQ+CgogIDwhLS0gY29tcG9zaXRpb24gbGluZTogY2xpZW50IHNpdHMgYWJvdmUgTGF5ZXIgMSAobm8gYXJyb3doZWFkIOKAlCB0aGlzIG1hcCBpcyBub3QgYSBmbG93KSAtLT4KICA8bGluZSB4MT0iNDQwIiB5MT0iMTMwIiB4Mj0iNDQwIiB5Mj0iMjAwIiBzdHJva2U9IiNDMkNDRDYiIHN0cm9rZS13aWR0aD0iMS41Ii8+CgogIDwhLS0gQUtTIGNsdXN0ZXIgYm91bmRhcnkgLS0+CiAgPHJlY3QgeD0iNDAiIHk9IjE1MCIgd2lkdGg9IjYyMCIgaGVpZ2h0PSI2NTYiIHJ4PSIxNCIgZmlsbD0iI0Y1RkFGRSIgc3Ryb2tlPSIjMDA3OEQ0IiBzdHJva2Utd2lkdGg9IjEuNCIgc3Ryb2tlLWRhc2hhcnJheT0iNyA1Ii8+CiAgPHJlY3QgeD0iNTIiIHk9IjE2MCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiByeD0iNCIgZmlsbD0iIzAwNzhENCIvPgogIDx0ZXh0IHg9Ijc2IiB5PSIxNzMiIGZvbnQtc2l6ZT0iMTIuNSIgY2xhc3M9InQiPkF6dXJlIEt1YmVybmV0ZXMgU2VydmljZSAoQUtTKSBjbHVzdGVyPC90ZXh0PgoKICA8IS0tID09PT09PT09PT09PSBMYXllciAxID09PT09PT09PT09PSAtLT4KICA8cmVjdCB4PSI1OCIgeT0iMTkwIiB3aWR0aD0iNTkyIiBoZWlnaHQ9Ijk0IiByeD0iMTAiIGZpbGw9IiNFQUYzRkIiLz4KICA8Y2lyY2xlIGN4PSI5NiIgY3k9IjIzOCIgcj0iMTUiIGZpbGw9IiMwMDc4RDQiLz4KICA8dGV4dCB4PSI5NiIgeT0iMjQzIiBmb250LXNpemU9IjE1IiBmaWxsPSIjZmZmZmZmIiBmb250LXdlaWdodD0iNzAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4xPC90ZXh0PgogIDx0ZXh0IHg9IjExOCIgeT0iMjMyIiBmb250LXNpemU9IjEyIiBjbGFzcz0idCI+TGF5ZXIgMSDigJQgU2VtYW50aWMgZGVjaXNpb248L3RleHQ+CiAgPHRleHQgeD0iMTE4IiB5PSIyNTAiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPm93bnM6IHdoaWNoIG1vZGVsIMK3IHNpZ25hbDogY29udGVudDwvdGV4dD4KCiAgPHJlY3QgeD0iMzMwIiB5PSIyMDAiIHdpZHRoPSIyODAiIGhlaWdodD0iNzYiIHJ4PSIxMCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjQzlENkUyIiBzdHJva2Utd2lkdGg9IjEuMyIvPgogIDxyZWN0IHg9IjM0MiIgeT0iMjE0IiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHJ4PSI0IiBmaWxsPSIjNjQ3NDhCIi8+CiAgPHRleHQgeD0iMzcwIiB5PSIyMjYiIGZvbnQtc2l6ZT0iMTQiIGNsYXNzPSJ0Ij5Sb3V0ZUxMTTwvdGV4dD4KICA8dGV4dCB4PSIzNzAiIHk9IjI0NCIgZm9udC1zaXplPSIxMC41IiBjbGFzcz0icyI+bWF0cml4LWZhY3Rvcml6YXRpb24gcm91dGVyPC90ZXh0PgogIDx0ZXh0IHg9IjM3MCIgeT0iMjYxIiBmb250LXNpemU9IjEwIiBmaWxsPSIjMDA3OEQ0Ij5waWNrcyB0aGUgbW9kZWwgY2xhc3M6IHN0cm9uZyB8IHdlYWs8L3RleHQ+CiAgPHJlY3QgeD0iNTU4IiB5PSIyMDgiIHdpZHRoPSI0NCIgaGVpZ2h0PSIxNyIgcng9IjgiIGZpbGw9IiNGMUY0RjciIHN0cm9rZT0iI0NCRDVERiIvPgogIDx0ZXh0IHg9IjU4MCIgeT0iMjIwIiBmb250LXNpemU9IjkuNSIgY2xhc3M9InMiIHRleHQtYW5jaG9yPSJtaWRkbGUiPk9TUzwvdGV4dD4KCiAgPCEtLSBjb21wb3NpdGlvbiBsaW5lIEwx4oaUTDIgLS0+CiAgPGxpbmUgeDE9IjQ3MCIgeTE9IjI3NiIgeDI9IjQ3MCIgeTI9IjMwOCIgc3Ryb2tlPSIjQzJDQ0Q2IiBzdHJva2Utd2lkdGg9IjEuNSIvPgoKICA8IS0tID09PT09PT09PT09PSBMYXllciAyID09PT09PT09PT09PSAtLT4KICA8cmVjdCB4PSI1OCIgeT0iMjk4IiB3aWR0aD0iNTkyIiBoZWlnaHQ9Ijk0IiByeD0iMTAiIGZpbGw9IiNFQUYzRkIiLz4KICA8Y2lyY2xlIGN4PSI5NiIgY3k9IjM0NiIgcj0iMTUiIGZpbGw9IiMwMDc4RDQiLz4KICA8dGV4dCB4PSI5NiIgeT0iMzUxIiBmb250LXNpemU9IjE1IiBmaWxsPSIjZmZmZmZmIiBmb250LXdlaWdodD0iNzAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4yPC90ZXh0PgogIDx0ZXh0IHg9IjExOCIgeT0iMzQwIiBmb250LXNpemU9IjEyIiBjbGFzcz0idCI+TGF5ZXIgMiDigJQgQUkgZ2F0ZXdheTwvdGV4dD4KICA8dGV4dCB4PSIxMTgiIHk9IjM1OCIgZm9udC1zaXplPSIxMC41IiBjbGFzcz0icyI+b3duczogYXV0aCDCtyBjb3N0IMK3IGd1YXJkcmFpbHMgwrcgc2lnbmFsOiBwb2xpY3k8L3RleHQ+CgogIDxyZWN0IHg9IjMzMCIgeT0iMzA4IiB3aWR0aD0iMjgwIiBoZWlnaHQ9Ijc2IiByeD0iMTAiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iI0M5RDZFMiIgc3Ryb2tlLXdpZHRoPSIxLjMiLz4KICA8cmVjdCB4PSIzNDIiIHk9IjMyMiIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiByeD0iNCIgZmlsbD0iIzY0NzQ4QiIvPgogIDx0ZXh0IHg9IjM3MCIgeT0iMzM0IiBmb250LXNpemU9IjE0IiBjbGFzcz0idCI+YWdlbnRnYXRld2F5PC90ZXh0PgogIDx0ZXh0IHg9IjM3MCIgeT0iMzUyIiBmb250LXNpemU9IjEwLjUiIGNsYXNzPSJzIj5PcGVuQUktY29tcGF0aWJsZSBkYXRhIHBsYW5lPC90ZXh0PgogIDx0ZXh0IHg9IjM3MCIgeT0iMzY5IiBmb250LXNpemU9IjEwIiBmaWxsPSIjMDA3OEQ0Ij5hYnN0cmFjdHMgYm90aCBiYWNrZW5kcyBiZWhpbmQgb25lIEFQSTwvdGV4dD4KICA8cmVjdCB4PSI1NTgiIHk9IjMxNiIgd2lkdGg9IjQ0IiBoZWlnaHQ9IjE3IiByeD0iOCIgZmlsbD0iI0YxRjRGNyIgc3Ryb2tlPSIjQ0JENURGIi8+CiAgPHRleHQgeD0iNTgwIiB5PSIzMjgiIGZvbnQtc2l6ZT0iOS41IiBjbGFzcz0icyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+T1NTPC90ZXh0PgoKICA8IS0tIHN0cnVjdHVyYWwgbGluazogZ2F0ZXdheSBhYnN0cmFjdHMgdGhlIGZyb250aWVyIGJhY2tlbmQgKG1hbmFnZWQsIGV4dGVybmFsKSAtLT4KICA8bGluZSB4MT0iNjEwIiB5MT0iMzQ2IiB4Mj0iNjg2IiB5Mj0iMzUyIiBzdHJva2U9IiMwMDc4RDQiIHN0cm9rZS13aWR0aD0iMS40Ii8+CiAgPHRleHQgeD0iNjE2IiB5PSIzMzgiIGZvbnQtc2l6ZT0iOS41IiBjbGFzcz0icmVsIj5mcm9udGllciBiYWNrZW5kPC90ZXh0PgoKICA8IS0tIHN0cnVjdHVyYWwgbGluazogZ2F0ZXdheSBhYnN0cmFjdHMgdGhlIHNlbGYtaG9zdGVkIGJhY2tlbmQgKExheWVyIDMpIC0tPgogIDxsaW5lIHgxPSI0NzAiIHkxPSIzODQiIHgyPSI0NzAiIHkyPSI0MTIiIHN0cm9rZT0iI0MyQ0NENiIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSI0ODIiIHk9IjQwMyIgZm9udC1zaXplPSI5LjUiIGNsYXNzPSJyZWwiPmNhbGxzIChleHQtcHJvYyk8L3RleHQ+CgogIDwhLS0gPT09PT09PT09PT09IExheWVyIDMgPT09PT09PT09PT09IC0tPgogIDxyZWN0IHg9IjU4IiB5PSI0MDQiIHdpZHRoPSI1OTIiIGhlaWdodD0iMzkyIiByeD0iMTAiIGZpbGw9IiNFQUYzRkIiLz4KICA8Y2lyY2xlIGN4PSI5NiIgY3k9IjQ0NiIgcj0iMTUiIGZpbGw9IiMwMDc4RDQiLz4KICA8dGV4dCB4PSI5NiIgeT0iNDUxIiBmb250LXNpemU9IjE1IiBmaWxsPSIjZmZmZmZmIiBmb250LXdlaWdodD0iNzAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4zPC90ZXh0PgogIDx0ZXh0IHg9IjExOCIgeT0iNDQwIiBmb250LXNpemU9IjEyIiBjbGFzcz0idCI+TGF5ZXIgMyDigJQgSW5mZXJlbmNlLWF3YXJlIHNlcnZpbmc8L3RleHQ+CiAgPHRleHQgeD0iMTE4IiB5PSI0NTgiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPm93bnM6IHdoaWNoIHJlcGxpY2Egwrcgc2lnbmFsOiBHUFUgc3RhdGU8L3RleHQ+CgogIDwhLS0gQ2FyZCBBOiBJbmZlcmVuY2UgRXh0ZW5zaW9uIC8gRVBQIChhZ2VudGdhdGV3YXkgY2FsbHMgaXQgZGlyZWN0bHkpIC0tPgogIDxyZWN0IHg9IjMzMCIgeT0iNDE2IiB3aWR0aD0iMjgwIiBoZWlnaHQ9Ijc0IiByeD0iMTAiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iI0M5RDZFMiIgc3Ryb2tlLXdpZHRoPSIxLjMiLz4KICA8cmVjdCB4PSIzNDIiIHk9IjQzMCIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiByeD0iNCIgZmlsbD0iIzY0NzQ4QiIvPgogIDx0ZXh0IHg9IjM3MCIgeT0iNDQyIiBmb250LXNpemU9IjEzLjUiIGNsYXNzPSJ0Ij5JbmZlcmVuY2UgRXh0ZW5zaW9uPC90ZXh0PgogIDx0ZXh0IHg9IjM3MCIgeT0iNDYwIiBmb250LXNpemU9IjEwLjUiIGNsYXNzPSJzIj5FbmRwb2ludCBQaWNrZXIg4oCUIHBpY2tzIHBvZCBvbjo8L3RleHQ+CiAgPHRleHQgeD0iMzcwIiB5PSI0NzYiIGZvbnQtc2l6ZT0iMTAiIGZpbGw9IiMwMDY3QjgiPktWLWNhY2hlIMK3IHF1ZXVlIGRlcHRoIMK3IExvUkE8L3RleHQ+CiAgPHJlY3QgeD0iNTU4IiB5PSI0MjQiIHdpZHRoPSI0NCIgaGVpZ2h0PSIxNyIgcng9IjgiIGZpbGw9IiNGMUY0RjciIHN0cm9rZT0iI0NCRDVERiIvPgogIDx0ZXh0IHg9IjU4MCIgeT0iNDM2IiBmb250LXNpemU9IjkuNSIgY2xhc3M9InMiIHRleHQtYW5jaG9yPSJtaWRkbGUiPk9TUzwvdGV4dD4KCiAgPCEtLSBjb21wb3NpdGlvbjogdGhlIHBpY2tlciBzZWxlY3RzIGEgcmVwbGljYSBmcm9tIHRoZSBLQUlUTyBwb29sIC0tPgogIDxsaW5lIHgxPSI0NzAiIHkxPSI0OTAiIHgyPSI0NzAiIHkyPSI1ODAiIHN0cm9rZT0iI0MyQ0NENiIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSI0ODIiIHk9IjUzOCIgZm9udC1zaXplPSI5LjUiIGNsYXNzPSJyZWwiPnNlbGVjdHMgcmVwbGljYSBmcm9tPC90ZXh0PgoKICA8IS0tIENhcmQgQzogS0FJVE8gLS0+CiAgPHJlY3QgeD0iMzMwIiB5PSI1ODAiIHdpZHRoPSIyODAiIGhlaWdodD0iNjYiIHJ4PSIxMCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjQzlENkUyIiBzdHJva2Utd2lkdGg9IjEuMyIvPgogIDxyZWN0IHg9IjM0MiIgeT0iNTk0IiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHJ4PSI0IiBmaWxsPSIjMDA3OEQ0Ii8+CiAgPHRleHQgeD0iMzcwIiB5PSI2MDYiIGZvbnQtc2l6ZT0iMTMuNSIgY2xhc3M9InQiPktBSVRPIFdvcmtzcGFjZTwvdGV4dD4KICA8dGV4dCB4PSIzNzAiIHk9IjYyNCIgZm9udC1zaXplPSIxMC41IiBjbGFzcz0icyI+QUkgdG9vbGNoYWluIG9wZXJhdG9yIGFkZC1vbiDCtyB2TExNPC90ZXh0PgogIDxyZWN0IHg9IjU0MCIgeT0iNTg4IiB3aWR0aD0iNjIiIGhlaWdodD0iMTciIHJ4PSI4IiBmaWxsPSIjRTZGMkZCIiBzdHJva2U9IiNCOERDRjUiLz4KICA8dGV4dCB4PSI1NzEiIHk9IjYwMCIgZm9udC1zaXplPSI5LjUiIGZpbGw9IiMwMDY3QjgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPk1hbmFnZWQ8L3RleHQ+CgogIDwhLS0gY29tcG9zaXRpb246IEtBSVRPIHNlcnZlcyB0aGUgcG9kIGZsZWV0IC0tPgogIDxsaW5lIHgxPSI0NzAiIHkxPSI2NDYiIHgyPSI0NzAiIHkyPSI2NjQiIHN0cm9rZT0iI0MyQ0NENiIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSI0ODIiIHk9IjY1OCIgZm9udC1zaXplPSI5LjUiIGNsYXNzPSJyZWwiPnNlcnZlczwvdGV4dD4KCiAgPCEtLSB2TExNIHBvZHMgLS0+CiAgPGcgZm9udC1zaXplPSIxMS41Ij4KICAgIDxyZWN0IHg9IjMzNSIgeT0iNjY2IiB3aWR0aD0iODAiIGhlaWdodD0iNDQiIHJ4PSI4IiBmaWxsPSIjRTZGMkZCIiBzdHJva2U9IiMwMDc4RDQiIHN0cm9rZS13aWR0aD0iMS4yIi8+CiAgICA8dGV4dCB4PSIzNzUiIHk9IjY5MiIgZmlsbD0iIzAwNjdCOCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+dkxMTTwvdGV4dD4KICAgIDxyZWN0IHg9IjQzMCIgeT0iNjY2IiB3aWR0aD0iODAiIGhlaWdodD0iNDQiIHJ4PSI4IiBmaWxsPSIjRTZGMkZCIiBzdHJva2U9IiMwMDc4RDQiIHN0cm9rZS13aWR0aD0iMS4yIi8+CiAgICA8dGV4dCB4PSI0NzAiIHk9IjY5MiIgZmlsbD0iIzAwNjdCOCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+dkxMTTwvdGV4dD4KICAgIDxyZWN0IHg9IjUyNSIgeT0iNjY2IiB3aWR0aD0iODAiIGhlaWdodD0iNDQiIHJ4PSI4IiBmaWxsPSIjRTZGMkZCIiBzdHJva2U9IiMwMDc4RDQiIHN0cm9rZS13aWR0aD0iMS4yIi8+CiAgICA8dGV4dCB4PSI1NjUiIHk9IjY5MiIgZmlsbD0iIzAwNjdCOCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+dkxMTTwvdGV4dD4KICA8L2c+CiAgPHRleHQgeD0iMzM1IiB5PSI3MzAiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPnRoZSAid2VhayIgLyBzZWxmLWhvc3RlZCBtb2RlbCDCtyBHUFUgbm9kZSBwb29sIHByb3Zpc2lvbmVkICZhbXA7IG1hbmFnZWQgYnkgS0FJVE88L3RleHQ+CgogIDwhLS0gPT09PT09PT09PT09IEV4dGVybmFsIG1hbmFnZWQgc2VydmljZXMgKHJpZ2h0KSA9PT09PT09PT09PT0gLS0+CiAgPCEtLSBBenVyZSBPcGVuQUkgLS0+CiAgPHJlY3QgeD0iNjg4IiB5PSIzMTgiIHdpZHRoPSIyMTIiIGhlaWdodD0iNzQiIHJ4PSIxMCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjQzlENkUyIiBzdHJva2Utd2lkdGg9IjEuMyIvPgogIDxyZWN0IHg9IjcwMCIgeT0iMzMyIiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHJ4PSI0IiBmaWxsPSIjMDA3OEQ0Ii8+CiAgPHRleHQgeD0iNzI4IiB5PSIzNDQiIGZvbnQtc2l6ZT0iMTQiIGNsYXNzPSJ0Ij5BenVyZSBPcGVuQUk8L3RleHQ+CiAgPHRleHQgeD0iNzI4IiB5PSIzNjIiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPmdwdC01LjEgZGVwbG95bWVudDwvdGV4dD4KICA8dGV4dCB4PSI3MjgiIHk9IjM3OSIgZm9udC1zaXplPSIxMCIgZmlsbD0iIzAwNjdCOCI+dGhlICJzdHJvbmciIC8gZnJvbnRpZXIgbW9kZWw8L3RleHQ+CiAgPHJlY3QgeD0iODM2IiB5PSIzMjYiIHdpZHRoPSI1NiIgaGVpZ2h0PSIxNyIgcng9IjgiIGZpbGw9IiNFNkYyRkIiIHN0cm9rZT0iI0I4RENGNSIvPgogIDx0ZXh0IHg9Ijg2NCIgeT0iMzM4IiBmb250LXNpemU9IjkuNSIgZmlsbD0iIzAwNjdCOCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+TWFuYWdlZDwvdGV4dD4KCiAgPCEtLSBNYW5hZ2VkIG1vbml0b3JpbmcgLS0+CiAgPHJlY3QgeD0iNjg4IiB5PSI0NzAiIHdpZHRoPSIyMTIiIGhlaWdodD0iOTIiIHJ4PSIxMCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjQzlENkUyIiBzdHJva2Utd2lkdGg9IjEuMyIvPgogIDxyZWN0IHg9IjcwMCIgeT0iNDg0IiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHJ4PSI0IiBmaWxsPSIjMDA3OEQ0Ii8+CiAgPHRleHQgeD0iNzI4IiB5PSI0OTYiIGZvbnQtc2l6ZT0iMTMuNSIgY2xhc3M9InQiPkF6dXJlIE1vbml0b3I8L3RleHQ+CiAgPHRleHQgeD0iNzI4IiB5PSI1MTQiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPk1hbmFnZWQgUHJvbWV0aGV1cyArIEdyYWZhbmE8L3RleHQ+CiAgPHRleHQgeD0iNzI4IiB5PSI1MzEiIGZvbnQtc2l6ZT0iMTAiIGZpbGw9IiMwMDY3QjgiPnZMTE0gJmFtcDsgY29zdCBtZXRyaWNzPC90ZXh0PgogIDxyZWN0IHg9IjgzNiIgeT0iNDc4IiB3aWR0aD0iNTYiIGhlaWdodD0iMTciIHJ4PSI4IiBmaWxsPSIjRTZGMkZCIiBzdHJva2U9IiNCOERDRjUiLz4KICA8dGV4dCB4PSI4NjQiIHk9IjQ5MCIgZm9udC1zaXplPSI5LjUiIGZpbGw9IiMwMDY3QjgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPk1hbmFnZWQ8L3RleHQ+CgogIDwhLS0gbWV0cmljcyBzaWRlLWNoYW5uZWw6IGRhc2hlZCArIGFycm93LCBkZWxpYmVyYXRlbHkgZGlzdGluY3QgZnJvbSBzb2xpZCBjb21wb3NpdGlvbiBsaW5lcyAtLT4KICA8cGF0aCBkPSJNNjEwLDYxMyBDNjYwLDYxMyA2NzIsNTI4IDY4Niw1MTIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzlBQThCNiIgc3Ryb2tlLXdpZHRoPSIxLjMiIHN0cm9rZS1kYXNoYXJyYXk9IjUgNCIgbWFya2VyLWVuZD0idXJsKCNhcikiLz4KICA8dGV4dCB4PSI2MjgiIHk9IjU2MCIgZm9udC1zaXplPSIxMCIgY2xhc3M9InMiPnNjcmFwZXM8L3RleHQ+CgogIDwhLS0gZm9vdGVyIC0tPgogIDx0ZXh0IHg9IjQwIiB5PSI4MjgiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPlNvbGlkIGxpbmVzIHNob3cgY29tcG9zaXRpb24gJmFtcDsgb3duZXJzaGlwLCBub3QgcmVxdWVzdCBvcmRlciDigJQgc2VlIHRoZSByZXF1ZXN0LXBhdGggZGlhZ3JhbSBmb3IgdGhlIHBlci1yZXF1ZXN0IHRyYWNlLiBEYXNoZWQgPSB0ZWxlbWV0cnkuPC90ZXh0Pgo8L3N2Zz4K" width="940" height="850" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-architecture-on-aks">The architecture on AKS<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#the-architecture-on-aks" class="hash-link" aria-label="Direct link to The architecture on AKS" title="Direct link to The architecture on AKS" translate="no">​</a></h2>
<p>Most slots are managed Azure; where an open-source piece has a managed alternative, it's called out in the layer that earns it:</p>
<table><thead><tr><th>Layer</th><th>Decision</th><th>Featured component</th><th>Managed on AKS?</th></tr></thead><tbody><tr><td>1 — Semantic</td><td>which <em>model</em> answers</td><td>RouteLLM</td><td>Open source on AKS</td></tr><tr><td>2 — AI gateway</td><td>auth, cost, guardrails, provider abstraction, inference-aware front door</td><td>agentgateway</td><td>Open source on AKS</td></tr><tr><td>3 — Inference LB</td><td>which <em>replica</em></td><td>Gateway API Inference Extension (Endpoint Picker)</td><td>Open source, called by agentgateway</td></tr><tr><td>3 — Serving</td><td>run the model on GPUs</td><td><strong>KAITO</strong> add-on → vLLM</td><td>Managed add-on</td></tr><tr><td>Frontier path</td><td>the "strong" model</td><td><strong>Azure OpenAI</strong></td><td>Managed</td></tr><tr><td>Observability</td><td>metrics &amp; dashboards</td><td><strong>Azure Managed Prometheus + Grafana</strong></td><td>Managed</td></tr></tbody></table>
<p><img decoding="async" loading="lazy" alt="Per-request trace through the three layers, numbered in order: an agent call (step 1) enters RouteLLM, which reads the prompt and picks strong or weak (step 2); agentgateway dispatches the strong path straight to Azure OpenAI (step 3a, ends there) or, on the weak path (step 3b), calls the Inference Extension Endpoint Picker over ext-proc (step 4), which selects one KAITO-served vLLM pod by GPU state that agentgateway then forwards to (step 5). The strong path ends at step 3a; the weak path ends at step 5." src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgODIwIDYyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxtYXJrZXIgaWQ9ImFyIiBtYXJrZXJXaWR0aD0iOSIgbWFya2VySGVpZ2h0PSI5IiByZWZYPSI2IiByZWZZPSIzIiBvcmllbnQ9ImF1dG8iIG1hcmtlclVuaXRzPSJzdHJva2VXaWR0aCI+CiAgICAgIDxwYXRoIGQ9Ik0wLDAgTDYsMyBMMCw2IHoiIGZpbGw9IiM1QjZCN0IiLz4KICAgIDwvbWFya2VyPgogICAgPG1hcmtlciBpZD0iYXJCIiBtYXJrZXJXaWR0aD0iOSIgbWFya2VySGVpZ2h0PSI5IiByZWZYPSI2IiByZWZZPSIzIiBvcmllbnQ9ImF1dG8iIG1hcmtlclVuaXRzPSJzdHJva2VXaWR0aCI+CiAgICAgIDxwYXRoIGQ9Ik0wLDAgTDYsMyBMMCw2IHoiIGZpbGw9IiMwMDc4RDQiLz4KICAgIDwvbWFya2VyPgogICAgPHN0eWxlPgogICAgICB0ZXh0IHsgZm9udC1mYW1pbHk6ICJTZWdvZSBVSSIsIC1hcHBsZS1zeXN0ZW0sIHN5c3RlbS11aSwgIkhlbHZldGljYSBOZXVlIiwgc2Fucy1zZXJpZjsgfQogICAgICAudCB7IGZpbGw6IzI0M0E1RTsgZm9udC13ZWlnaHQ6NjAwOyB9CiAgICAgIC5zIHsgZmlsbDojNUI2QjdCOyB9CiAgICAgIC5zdGVwIHsgZmlsbDojNUI2QjdCOyBmb250LXdlaWdodDo3MDA7IH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgoKICA8cmVjdCB4PSIyIiB5PSIyIiB3aWR0aD0iODE2IiBoZWlnaHQ9IjYxNiIgcng9IjE0IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiNFMkU4RjAiIHN0cm9rZS13aWR0aD0iMS41Ii8+CgogIDx0ZXh0IHg9IjMyIiB5PSI0MCIgZm9udC1zaXplPSIyMCIgY2xhc3M9InQiPkFnZW50IGNhbGwgwrcgb25lIHJlcXVlc3QsIGluIG9yZGVyPC90ZXh0PgogIDx0ZXh0IHg9IjMyIiB5PSI1OSIgZm9udC1zaXplPSIxMiIgY2xhc3M9InMiPnRoZSBwZXItY2FsbCB0cmFjZSB0aHJvdWdoIHRoZSB0aHJlZSBsYXllcnMg4oCUIGZvciB3aG8tb3ducy13aGF0IGFuZCBtYW5hZ2VkIHZzLiBPU1MsIHNlZSB0aGUgc3RydWN0dXJlIGRpYWdyYW08L3RleHQ+CgogIDwhLS0gc3RlcC1iYWRnZSBoZWxwZXIgbG9vazogc21hbGwgY2lyY2xlcyBvbiB0aGUgZmxvdyBhcnJvd3MgLS0+CgogIDwhLS0gQ2xpZW50IC0tPgogIDxyZWN0IHg9IjEzMCIgeT0iODAiIHdpZHRoPSIyODAiIGhlaWdodD0iNDgiIHJ4PSIxMCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjQzlENkUyIiBzdHJva2Utd2lkdGg9IjEuMyIvPgogIDxyZWN0IHg9IjE0MiIgeT0iOTQiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgcng9IjQiIGZpbGw9IiM2NDc0OEIiLz4KICA8dGV4dCB4PSIxNzAiIHk9IjEwOSIgZm9udC1zaXplPSIxMy41IiBjbGFzcz0idCI+QWdlbnQgLyBhcHA8L3RleHQ+CgogIDwhLS0gZmxvdyAxIC0tPgogIDxsaW5lIHgxPSIyNzAiIHkxPSIxMjgiIHgyPSIyNzAiIHkyPSIxNjAiIHN0cm9rZT0iIzVCNkI3QiIgc3Ryb2tlLXdpZHRoPSIxLjUiIG1hcmtlci1lbmQ9InVybCgjYXIpIi8+CiAgPGNpcmNsZSBjeD0iMjQ4IiBjeT0iMTQ0IiByPSI5IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiM1QjZCN0IiIHN0cm9rZS13aWR0aD0iMS4zIi8+CiAgPHRleHQgeD0iMjQ4IiB5PSIxNDgiIGZvbnQtc2l6ZT0iMTAiIGNsYXNzPSJzdGVwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4xPC90ZXh0PgogIDx0ZXh0IHg9IjI4OCIgeT0iMTQwIiBmb250LXNpemU9IjEwIiBjbGFzcz0icyI+UE9TVCAvdjEvY2hhdC9jb21wbGV0aW9uczwvdGV4dD4KICA8dGV4dCB4PSIyODgiIHk9IjE1MyIgZm9udC1zaXplPSIxMCIgY2xhc3M9InMiPm1vZGVsOiAicm91dGVyLW1mLTAuMTE1OTMiPC90ZXh0PgoKICA8IS0tIExheWVyIDE6IFJvdXRlTExNIC0tPgogIDxjaXJjbGUgY3g9IjEwMCIgY3k9IjE5NiIgcj0iMTMiIGZpbGw9IiMwMDc4RDQiLz4KICA8dGV4dCB4PSIxMDAiIHk9IjIwMSIgZm9udC1zaXplPSIxMyIgZmlsbD0iI2ZmZmZmZiIgZm9udC13ZWlnaHQ9IjcwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+MTwvdGV4dD4KICA8cmVjdCB4PSIxMzAiIHk9IjE2MCIgd2lkdGg9IjI4MCIgaGVpZ2h0PSI3MiIgcng9IjEwIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiNDOUQ2RTIiIHN0cm9rZS13aWR0aD0iMS4zIi8+CiAgPHJlY3QgeD0iMTQyIiB5PSIxNzYiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgcng9IjQiIGZpbGw9IiM2NDc0OEIiLz4KICA8dGV4dCB4PSIxNzAiIHk9IjE4OCIgZm9udC1zaXplPSIxMy41IiBjbGFzcz0idCI+Um91dGVMTE08L3RleHQ+CiAgPHRleHQgeD0iMTcwIiB5PSIyMDYiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPnNlbWFudGljIGRlY2lzaW9uIChtYXRyaXgtZmFjdG9yaXphdGlvbik8L3RleHQ+CiAgPHRleHQgeD0iMTcwIiB5PSIyMjMiIGZvbnQtc2l6ZT0iMTAuNSIgZmlsbD0iIzAwNzhENCI+cmVhZHMgcHJvbXB0IOKGkiBwaWNrcyBzdHJvbmcgfCB3ZWFrPC90ZXh0PgoKICA8IS0tIGZsb3cgMiAtLT4KICA8bGluZSB4MT0iMjcwIiB5MT0iMjMyIiB4Mj0iMjcwIiB5Mj0iMjYyIiBzdHJva2U9IiM1QjZCN0IiIHN0cm9rZS13aWR0aD0iMS41IiBtYXJrZXItZW5kPSJ1cmwoI2FyKSIvPgogIDxjaXJjbGUgY3g9IjI0OCIgY3k9IjI0NyIgcj0iOSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjNUI2QjdCIiBzdHJva2Utd2lkdGg9IjEuMyIvPgogIDx0ZXh0IHg9IjI0OCIgeT0iMjUxIiBmb250LXNpemU9IjEwIiBjbGFzcz0ic3RlcCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+MjwvdGV4dD4KICA8dGV4dCB4PSIyODgiIHk9IjI1MSIgZm9udC1zaXplPSIxMCIgY2xhc3M9InMiPmZvcndhcmRzIG1vZGVsOiBzdHJvbmcgfCB3ZWFrPC90ZXh0PgoKICA8IS0tIExheWVyIDI6IGFnZW50Z2F0ZXdheSAtLT4KICA8Y2lyY2xlIGN4PSIxMDAiIGN5PSIyOTgiIHI9IjEzIiBmaWxsPSIjMDA3OEQ0Ii8+CiAgPHRleHQgeD0iMTAwIiB5PSIzMDMiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiNmZmZmZmYiIGZvbnQtd2VpZ2h0PSI3MDAiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjI8L3RleHQ+CiAgPHJlY3QgeD0iMTMwIiB5PSIyNjIiIHdpZHRoPSIyODAiIGhlaWdodD0iNzIiIHJ4PSIxMCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjQzlENkUyIiBzdHJva2Utd2lkdGg9IjEuMyIvPgogIDxyZWN0IHg9IjE0MiIgeT0iMjc4IiB3aWR0aD0iMTgiIGhlaWdodD0iMTgiIHJ4PSI0IiBmaWxsPSIjNjQ3NDhCIi8+CiAgPHRleHQgeD0iMTcwIiB5PSIyOTAiIGZvbnQtc2l6ZT0iMTMuNSIgY2xhc3M9InQiPmFnZW50Z2F0ZXdheTwvdGV4dD4KICA8dGV4dCB4PSIxNzAiIHk9IjMwOCIgZm9udC1zaXplPSIxMC41IiBjbGFzcz0icyI+YXV0aCDCtyBidWRnZXQgwrcgZ3VhcmRyYWlscyDCtyBPVGVsPC90ZXh0PgogIDx0ZXh0IHg9IjE3MCIgeT0iMzI1IiBmb250LXNpemU9IjEwLjUiIGZpbGw9IiMwMDc4RDQiPmRpc3BhdGNoZXMgYnkgbW9kZWwgbmFtZTwvdGV4dD4KCiAgPCEtLSBzdHJvbmcgYnJhbmNoOiBzdGVwIDNhIC0tPgogIDxsaW5lIHgxPSI0MTAiIHkxPSIyOTgiIHgyPSI0OTgiIHkyPSIyOTgiIHN0cm9rZT0iIzAwNzhENCIgc3Ryb2tlLXdpZHRoPSIxLjYiIG1hcmtlci1lbmQ9InVybCgjYXJCKSIvPgogIDxjaXJjbGUgY3g9IjQ3MCIgY3k9IjMxMyIgcj0iOSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDA3OEQ0IiBzdHJva2Utd2lkdGg9IjEuMyIvPgogIDx0ZXh0IHg9IjQ3MCIgeT0iMzE3IiBmb250LXNpemU9IjkuNSIgZmlsbD0iIzAwNjdCOCIgZm9udC13ZWlnaHQ9IjcwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+M2E8L3RleHQ+CiAgPHRleHQgeD0iNDI4IiB5PSIyODgiIGZvbnQtc2l6ZT0iMTAuNSIgZmlsbD0iIzAwNjdCOCIgZm9udC13ZWlnaHQ9IjYwMCI+c3Ryb25nPC90ZXh0PgogIDxyZWN0IHg9IjUwMCIgeT0iMjYyIiB3aWR0aD0iMjUwIiBoZWlnaHQ9IjcyIiByeD0iMTAiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iI0M5RDZFMiIgc3Ryb2tlLXdpZHRoPSIxLjMiLz4KICA8cmVjdCB4PSI1MTIiIHk9IjI3OCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE4IiByeD0iNCIgZmlsbD0iIzAwNzhENCIvPgogIDx0ZXh0IHg9IjU0MCIgeT0iMjkwIiBmb250LXNpemU9IjEzLjUiIGNsYXNzPSJ0Ij5BenVyZSBPcGVuQUk8L3RleHQ+CiAgPHRleHQgeD0iNTQwIiB5PSIzMDgiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPmdwdC01LjEgZGVwbG95bWVudDwvdGV4dD4KICA8dGV4dCB4PSI1NDAiIHk9IjMyNSIgZm9udC1zaXplPSIxMC41IiBmaWxsPSIjMDA2N0I4Ij5mcm9udGllciBtb2RlbCDCtyBlbmRzIGhlcmU8L3RleHQ+CgogIDwhLS0gd2VhayBicmFuY2g6IHN0ZXAgM2IgLS0+CiAgPGxpbmUgeDE9IjI3MCIgeTE9IjMzNCIgeDI9IjI3MCIgeTI9IjM2NiIgc3Ryb2tlPSIjNUI2QjdCIiBzdHJva2Utd2lkdGg9IjEuNSIgbWFya2VyLWVuZD0idXJsKCNhcikiLz4KICA8Y2lyY2xlIGN4PSIyNDgiIGN5PSIzNTAiIHI9IjkiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzVCNkI3QiIgc3Ryb2tlLXdpZHRoPSIxLjMiLz4KICA8dGV4dCB4PSIyNDgiIHk9IjM1NCIgZm9udC1zaXplPSI5LjUiIGNsYXNzPSJzdGVwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4zYjwvdGV4dD4KICA8dGV4dCB4PSIyODgiIHk9IjM1NCIgZm9udC1zaXplPSIxMC41IiBjbGFzcz0icyIgZm9udC13ZWlnaHQ9IjYwMCI+d2VhazwvdGV4dD4KCiAgPCEtLSBMYXllciAzIGdyb3VwIC0tPgogIDxjaXJjbGUgY3g9IjEwMCIgY3k9IjM5OSIgcj0iMTMiIGZpbGw9IiMwMDc4RDQiLz4KICA8dGV4dCB4PSIxMDAiIHk9IjQwNCIgZm9udC1zaXplPSIxMyIgZmlsbD0iI2ZmZmZmZiIgZm9udC13ZWlnaHQ9IjcwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+MzwvdGV4dD4KCiAgPCEtLSBJbmZlcmVuY2UgRXh0ZW5zaW9uIChFUFApIOKAlCBhZ2VudGdhdGV3YXkgY2FsbHMgaXQgZGlyZWN0bHkgb24gdGhlIHdlYWsgcGF0aCAtLT4KICA8cmVjdCB4PSIxMzAiIHk9IjM2NiIgd2lkdGg9IjI4MCIgaGVpZ2h0PSI2NiIgcng9IjEwIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiNDOUQ2RTIiIHN0cm9rZS13aWR0aD0iMS4zIi8+CiAgPHJlY3QgeD0iMTQyIiB5PSIzODAiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgcng9IjQiIGZpbGw9IiM2NDc0OEIiLz4KICA8dGV4dCB4PSIxNzAiIHk9IjM5MiIgZm9udC1zaXplPSIxMy41IiBjbGFzcz0idCI+SW5mZXJlbmNlIEV4dGVuc2lvbiAoRVBQKTwvdGV4dD4KICA8dGV4dCB4PSIxNzAiIHk9IjQxMCIgZm9udC1zaXplPSIxMC41IiBjbGFzcz0icyI+cGlja3MgdGhlIGJlc3QgcG9kIGJ5PC90ZXh0PgogIDx0ZXh0IHg9IjE3MCIgeT0iNDI2IiBmb250LXNpemU9IjEwIiBmaWxsPSIjMDA2N0I4Ij5LVi1jYWNoZSAlIMK3IHF1ZXVlIGRlcHRoIMK3IExvUkEgYWZmaW5pdHk8L3RleHQ+CgogIDwhLS0gZmxvdyA0IC0tPgogIDxsaW5lIHgxPSIyNzAiIHkxPSI0MzIiIHgyPSIyNzAiIHkyPSI0NTgiIHN0cm9rZT0iIzVCNkI3QiIgc3Ryb2tlLXdpZHRoPSIxLjUiIG1hcmtlci1lbmQ9InVybCgjYXIpIi8+CiAgPGNpcmNsZSBjeD0iMjQ4IiBjeT0iNDQ1IiByPSI5IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiM1QjZCN0IiIHN0cm9rZS13aWR0aD0iMS4zIi8+CiAgPHRleHQgeD0iMjQ4IiB5PSI0NDkiIGZvbnQtc2l6ZT0iMTAiIGNsYXNzPSJzdGVwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj40PC90ZXh0PgoKICA8IS0tIEtBSVRPIC0tPgogIDxyZWN0IHg9IjEzMCIgeT0iNDU4IiB3aWR0aD0iMjgwIiBoZWlnaHQ9IjY2IiByeD0iMTAiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iI0M5RDZFMiIgc3Ryb2tlLXdpZHRoPSIxLjMiLz4KICA8cmVjdCB4PSIxNDIiIHk9IjQ3MiIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE4IiByeD0iNCIgZmlsbD0iIzAwNzhENCIvPgogIDx0ZXh0IHg9IjE3MCIgeT0iNDg0IiBmb250LXNpemU9IjEzLjUiIGNsYXNzPSJ0Ij5LQUlUTyBXb3Jrc3BhY2U8L3RleHQ+CiAgPHRleHQgeD0iMTcwIiB5PSI1MDIiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPkFJIHRvb2xjaGFpbiBvcGVyYXRvciBhZGQtb248L3RleHQ+CiAgPHRleHQgeD0iMTcwIiB5PSI1MTgiIGZvbnQtc2l6ZT0iMTAiIGZpbGw9IiMwMDY3QjgiPmFnZW50Z2F0ZXdheSBmb3J3YXJkcyB0byB0aGUgY2hvc2VuIHBvZDwvdGV4dD4KCiAgPCEtLSBmbG93IDUgLS0+CiAgPGxpbmUgeDE9IjI3MCIgeTE9IjUyNCIgeDI9IjI3MCIgeTI9IjU1MCIgc3Ryb2tlPSIjNUI2QjdCIiBzdHJva2Utd2lkdGg9IjEuNSIgbWFya2VyLWVuZD0idXJsKCNhcikiLz4KICA8Y2lyY2xlIGN4PSIyNDgiIGN5PSI1MzciIHI9IjkiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzVCNkI3QiIgc3Ryb2tlLXdpZHRoPSIxLjMiLz4KICA8dGV4dCB4PSIyNDgiIHk9IjU0MSIgZm9udC1zaXplPSIxMCIgY2xhc3M9InN0ZXAiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjU8L3RleHQ+CgogIDwhLS0gdkxMTSBwb2RzIC0tPgogIDxnIGZvbnQtc2l6ZT0iMTEuNSI+CiAgICA8cmVjdCB4PSIxMzgiIHk9IjU1MCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjQyIiByeD0iOCIgZmlsbD0iI0U2RjJGQiIgc3Ryb2tlPSIjMDA3OEQ0IiBzdHJva2Utd2lkdGg9IjEuMiIvPgogICAgPHRleHQgeD0iMTc4IiB5PSI1NzUiIGZpbGw9IiMwMDY3QjgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPnZMTE08L3RleHQ+CiAgICA8cmVjdCB4PSIyMzAiIHk9IjU1MCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjQyIiByeD0iOCIgZmlsbD0iI0U2RjJGQiIgc3Ryb2tlPSIjMDA3OEQ0IiBzdHJva2Utd2lkdGg9IjEuMiIvPgogICAgPHRleHQgeD0iMjcwIiB5PSI1NzUiIGZpbGw9IiMwMDY3QjgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPnZMTE08L3RleHQ+CiAgICA8cmVjdCB4PSIzMjIiIHk9IjU1MCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjQyIiByeD0iOCIgZmlsbD0iI0U2RjJGQiIgc3Ryb2tlPSIjMDA3OEQ0IiBzdHJva2Utd2lkdGg9IjEuMiIvPgogICAgPHRleHQgeD0iMzYyIiB5PSI1NzUiIGZpbGw9IiMwMDY3QjgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPnZMTE08L3RleHQ+CiAgPC9nPgogIDx0ZXh0IHg9IjQzMCIgeT0iNTY4IiBmb250LXNpemU9IjEwLjUiIGNsYXNzPSJzIj5vbmUgcG9kIGNob3NlbiBieSBHUFUgc3RhdGU8L3RleHQ+CiAgPHRleHQgeD0iNDMwIiB5PSI1ODMiIGZvbnQtc2l6ZT0iMTAuNSIgY2xhc3M9InMiPsK3IHdlYWsgcGF0aCBlbmRzIGhlcmU8L3RleHQ+CgogIDwhLS0gbGVnZW5kIC0tPgogIDxyZWN0IHg9IjUwMCIgeT0iMzkyIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjMDA3OEQ0Ii8+CiAgPHRleHQgeD0iNTIyIiB5PSI0MDUiIGZvbnQtc2l6ZT0iMTEiIGNsYXNzPSJ0IiBmb250LXdlaWdodD0iNDAwIj5tYW5hZ2VkIEF6dXJlPC90ZXh0PgogIDxyZWN0IHg9IjUwMCIgeT0iNDE0IiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiM2NDc0OEIiIHN0cm9rZS13aWR0aD0iMS41Ii8+CiAgPHRleHQgeD0iNTIyIiB5PSI0MjciIGZvbnQtc2l6ZT0iMTEiIGNsYXNzPSJ0IiBmb250LXdlaWdodD0iNDAwIj5vcGVuIHNvdXJjZSBvbiBBS1M8L3RleHQ+CiAgPHRleHQgeD0iNTAwIiB5PSI0NTIiIGZvbnQtc2l6ZT0iMTAiIGNsYXNzPSJzIj5OdW1iZXJlZCBiYWRnZXMgPSByZXF1ZXN0IG9yZGVyLjwvdGV4dD4KICA8dGV4dCB4PSI1MDAiIHk9IjQ2NiIgZm9udC1zaXplPSIxMCIgY2xhc3M9InMiPlR3byBlbmRpbmdzOiAzYSAoc3Ryb25nKSBvciA1ICh3ZWFrKS48L3RleHQ+Cjwvc3ZnPgoK" width="820" height="620" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>A NOTE ON EXACTNESS</div><div class="admonitionContent_BuS1"><p>Several pieces here are young, and field names drift between releases — the Inference Extension renamed and restructured CRDs on its way to v1. Everything below was validated end-to-end on AKS in mid-2026 against Inference Extension v1.0.0 and agentgateway v1.3.1. Treat the manifests as the <em>shape</em> of the solution, pin your versions, and confirm fields against the docs at the end. The decomposition is stable; specific flags and CRD fields move quickly.</p></div></div>
<p>With the map in hand, the rest of this post builds it — bottom-up, serving first and the semantic decision last, because each layer needs the address of the one beneath it. First, the cluster everything runs on.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="prerequisites">Prerequisites<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h2>
<ul>
<li>An Azure subscription with GPU quota for the VM family you'll serve on (this guide uses a single <code>Standard_NC*</code> GPU node pool, provisioned for you by KAITO). Quota isn't capacity — confirm the SKU is actually allocatable in your region before you rely on it.</li>
<li>A recent Azure CLI and <code>kubectl</code>.</li>
<li>An Azure OpenAI (or AI Foundry) resource with a current frontier deployment for the strong path. We use <code>gpt-5.1</code>; <code>gpt-4o</code> is deprecating in many regions and may not be newly deployable.</li>
</ul>
<p>Create a cluster with the serving and monitoring add-ons enabled. KAITO provisions and manages GPU node pools on demand, so you don't pre-create them:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az aks create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $RG \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name $CLUSTER \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --node-count 2 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-ai-toolchain-operator \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-oidc-issuer \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-azure-monitor-metrics \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --generate-ssh-keys</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az aks get-credentials --resource-group $RG --name $CLUSTER</span><br></span></code></pre></div></div>
<p>That's the whole cluster — no gateway add-on, no extra CRDs, no Istio to bootstrap. agentgateway is the front door (more on that in Layer 2). With the cluster up, start at the bottom: serving the weak model.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="layer-3--serving-and-the-load-balancing-trap">Layer 3 — Serving, and the load-balancing trap<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#layer-3--serving-and-the-load-balancing-trap" class="hash-link" aria-label="Direct link to Layer 3 — Serving, and the load-balancing trap" title="Direct link to Layer 3 — Serving, and the load-balancing trap" translate="no">​</a></h2>
<p>Start with the failure mode, because it's the whole reason this layer is more than a <code>Service</code> — and agents make it worse.</p>
<p>Round-robin assumes requests are interchangeable. LLM requests are anything but: a 100k-token context-stuffed agent step and a 200-token tool-argument fill differ by three orders of magnitude in cost. An agent fleet mixes both constantly. Spray them uniformly across identical pods and round-robin will cheerfully queue a cheap completion behind a giant prefill on a saturated pod while an idle pod sits next to it. Your throughput looks fine and your p99 — the thing an agent loop feels on <em>every</em> iteration — falls off a cliff.</p>
<p>The fix isn't more pods — it's <em>placing each request on the pod best able to take it right now</em>, by reading live KV-cache occupancy and queue depth. That capability is what the <strong>Endpoint Picker</strong> is. Everything else in this layer exists to feed it.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="31-the-pods-to-place-kaito">3.1 The pods to place: KAITO<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#31-the-pods-to-place-kaito" class="hash-link" aria-label="Direct link to 3.1 The pods to place: KAITO" title="Direct link to 3.1 The pods to place: KAITO" translate="no">​</a></h3>
<p>The Endpoint Picker needs a homogeneous pool of model servers exporting the right signals, and KAITO produces exactly that: declare a <code>Workspace</code>, point it at a preset, and KAITO provisions the GPU SKU, runs vLLM, and exposes an OpenAI-compatible Service. Because the engine is vLLM, every pod emits the metrics the Endpoint Picker routes on (<code>vllm:num_requests_waiting</code>, <code>vllm:kv_cache_usage_perc</code>). Apply <a href="https://github.com/Azure/AKS/blob/master/examples/llm-routing-on-aks/kaito-workspace.yaml" target="_blank" rel="noopener noreferrer"><code>kaito-workspace.yaml</code></a> — it pins <code>instanceType: Standard_NC24ads_A100_v4</code>, <code>count: 2</code>, and <code>preset: phi-4-mini-instruct</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl create namespace llm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f kaito-workspace.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get workspace workspace-phi-4-mini -n llm -w   # Ready can take ~10–20 min</span><br></span></code></pre></div></div>
<p>KAITO provisions the GPU node, pulls the model, and starts vLLM; the workspace flips to <code>Ready</code> when inference is up:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME                   INSTANCE                   RESOURCEREADY   INFERENCEREADY   STATE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">workspace-phi-4-mini   Standard_NC24ads_A100_v4   True            True             Ready</span><br></span></code></pre></div></div>
<p>KAITO publishes a Service named <code>workspace-phi-4-mini</code> on port 80 and labels its pods <code>kaito.sh/workspace: workspace-phi-4-mini</code> — that label is how the next step selects the pool. One detail that will bite you later: the Service maps port 80 to the vLLM <strong>container's port 5000</strong>, and the Endpoint Picker routes to pod IPs directly, bypassing the Service. So the pool below targets <code>5000</code>, not <code>80</code>.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="32-the-placement-logic-the-inference-extension">3.2 The placement logic: the Inference Extension<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#32-the-placement-logic-the-inference-extension" class="hash-link" aria-label="Direct link to 3.2 The placement logic: the Inference Extension" title="Direct link to 3.2 The placement logic: the Inference Extension" translate="no">​</a></h3>
<p>Pool membership — <em>which pods are the model server</em> — is a different concern from how a workload is weighted against that pool, so the Inference Extension splits them across two objects:</p>
<ul>
<li>An <strong><code>InferencePool</code></strong> — the set of model-server pods plus a reference to the <strong>Endpoint Picker (EPP)</strong>, the extension that an ext-proc-capable proxy calls to choose an endpoint per request. In this design that proxy is agentgateway (Layer 2); the InferencePool and EPP don't care who calls them.</li>
<li>An <strong><code>InferenceObjective</code></strong> — the per-workload treatment: an integer <code>priority</code> against a pool. It replaces the older <code>InferenceModel</code> object: where <code>InferenceModel</code> carried a <code>modelName</code> and a <code>criticality</code> enum, <code>InferenceObjective</code> keeps only an integer <code>priority</code> and takes the served-model name from the request instead.</li>
</ul>
<p>Install the CRDs and the Endpoint Picker (the project ships a Helm chart), pointing it at the KAITO pods. The EPP must serve <strong>plaintext</strong> gRPC to match agentgateway's ext-proc client — set <code>--secure-serving=false</code>, or the call to it resets before headers:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/releases/download/v1.0.0/manifests.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">helm install phi-epp \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --version v1.0.0 -n llm \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --set inferencePool.modelServers.matchLabels."kaito\.sh/workspace"=workspace-phi-4-mini \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --set provider.name=none \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --set inferenceExtension.extraArgs="{--secure-serving=false}"</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>KEEP THE PICKER CLUSTER-INTERNAL</div><div class="admonitionContent_BuS1"><p><code>--secure-serving=false</code> disables TLS on the Endpoint Picker, so the ext-proc hop is plaintext gRPC. It's the simplest way to match agentgateway's client, but only safe because the EPP is reached cluster-internally over a <code>ClusterIP</code> Service. Don't expose it beyond the cluster, and restrict who can reach it — a <code>NetworkPolicy</code> that admits only the agentgateway pods, or a service mesh that re-encrypts the hop.</p></div></div>
<p>The Helm chart creates its own <code>InferencePool</code> named after the release, so you don't strictly need to apply one. If you do, <a href="https://github.com/Azure/AKS/blob/master/examples/llm-routing-on-aks/inference-pool.yaml" target="_blank" rel="noopener noreferrer"><code>inference-pool.yaml</code></a> has the full <code>InferencePool</code> + <code>InferenceObjective</code> — with two fields that trip people up. The pool's selector needs a structured <code>matchLabels</code>, and its <code>targetPorts</code> must be the vLLM <strong>container</strong> port <code>5000</code> (the pool routes to pod IPs, bypassing the Service's port 80):</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">kaito.sh/workspace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> workspace</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">4</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mini </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">targetPorts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">number</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5000</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># the vLLM container port, NOT the Service's 80</span><br></span></code></pre></div></div>
<p>The other trap is the API group: <code>InferencePool</code> lives in <code>inference.networking.k8s.io</code>, but <code>InferenceObjective</code> lives in <code>inference.networking.x-k8s.io</code> — different groups, easy to miss.</p>
<p>The Endpoint Picker is now running and watching the pool, but nothing calls it yet. It needs an ext-proc-capable proxy out front to consult it per request — and that proxy is the AI gateway in Layer 2, so we wire the call there rather than standing up a separate Gateway API gateway just to reach it.</p>
<p><strong>What this layer decided:</strong> only <em>which replica</em>, on GPU state. It has no idea what a "strong" model is, no budgets, no auth. Those are the next layer's job.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="layer-2--the-gateway-and-why-agents-need-it-most">Layer 2 — The gateway, and why agents need it most<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#layer-2--the-gateway-and-why-agents-need-it-most" class="hash-link" aria-label="Direct link to Layer 2 — The gateway, and why agents need it most" title="Direct link to Layer 2 — The gateway, and why agents need it most" translate="no">​</a></h2>
<p>The tempting shortcut is to let the router call backends directly. Resist it — and with agents the reason is sharper than with chat. An agent loop can spend an unbounded number of tokens before it decides to stop; without a budget and a rate limit <em>somewhere on the path</em>, a single misbehaving agent can run up a frontier bill or saturate your GPU fleet. A gateway is where that policy lives, written once, so nothing else has to reinvent it.</p>
<p>agentgateway is an open-source, OpenAI-compatible AI gateway built for exactly this traffic: provider abstraction, auth, token-based rate limiting, model-cost tracking, guardrails, and OpenTelemetry across backends — and, because it's agent-native, first-class MCP tool-serving and agent-to-agent routing when you grow into them. Here it does three jobs: hide the <strong>strong</strong> (Azure OpenAI) and <strong>weak</strong> (in-cluster) backends behind one endpoint, enforce policy on every call, and — because it speaks ext-proc itself — <strong>call the Endpoint Picker directly</strong> so the weak path gets GPU-state placement. That third job is what lets us drop the Gateway API gateway entirely: one data plane instead of two, and no dependency on the managed gateway's inference-extension support, which is still preview-gated on AKS today.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>CONFIG SHAPE</div><div class="admonitionContent_BuS1"><p>The snippets below were validated against agentgateway v1.3.1 and trimmed for the argument; pin field names to the <a href="https://agentgateway.dev/docs/" target="_blank" rel="noopener noreferrer">agentgateway docs</a>. The full file is in the <a href="https://github.com/Azure/AKS/tree/master/examples/llm-routing-on-aks" target="_blank" rel="noopener noreferrer">example manifests</a>.</p></div></div>
<p>The outer shape is <code>binds → listeners → routes → backends</code>. The one thing to internalize: an AI backend is <code>ai: { name, provider: { &lt;provider&gt;: {...} } }</code> — the provider nests under <code>provider:</code>, <code>name</code> is required, and the Azure provider needs <code>resourceName</code> <strong>and</strong> <code>resourceType</code> (its <code>model</code> is your Azure <em>deployment</em> name). Auth attaches as a route policy whose key the Azure provider sends as the <code>api-key</code> header (a static key keeps the example self-contained; for production on AKS, prefer Azure <strong>workload identity</strong>, which agentgateway supports natively for the Azure provider):</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># the strong route's backend (excerpt)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">backends</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">ai</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> aoai</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">strong</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">provider</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">azure</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">resourceName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;your-aoai-resource&gt;"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">resourceType</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> openAI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">model</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gpt</span><span class="token punctuation" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">5.1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"2024-10-21"</span><br></span></code></pre></div></div>
<p>The <code>strong</code> route is a single <code>ai</code> backend pointed at Azure OpenAI. The <code>weak</code> route is where the front-door magic lives: a <code>service</code> backend pointed at the KAITO pods, with an <strong><code>inferenceRouting</code></strong> policy that hands each request to the Endpoint Picker over ext-proc and forwards to the pod it returns. Three details earned during validation, because the loader is strict about each:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># top-level: declare the model-server pool the picker selects from</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">weak</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> llm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">hostname</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> workspace</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">4</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mini.llm.svc.cluster.local</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">vips</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">                       </span><span class="token comment" style="color:#999988;font-style:italic"># required, even when empty</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">5000</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5000</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># the vLLM container port</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># ...the weak route's backend:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">backends</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">service</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> llm/workspace</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">4</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mini.llm.svc.cluster.local   </span><span class="token comment" style="color:#999988;font-style:italic"># namespace/HOSTNAME, not the service name</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">policies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">inferenceRouting</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># a BACKEND policy, not a route policy</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">endpointPicker</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">host</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">epp</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">epp.llm.svc.cluster.local</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">9002</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">destinationMode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> passthrough   </span><span class="token comment" style="color:#999988;font-style:italic"># trust the picker's choice</span><br></span></code></pre></div></div>
<p>The full two-route config — including the <code>strong</code> <code>ai</code> backend and both routes' policies — is in <a href="https://github.com/Azure/AKS/blob/master/examples/llm-routing-on-aks/agentgateway-config.yaml" target="_blank" rel="noopener noreferrer"><code>agentgateway-config.yaml</code></a>. The credential never leaves the gateway, and on the <code>weak</code> route agentgateway itself is the thing that consults the picker.</p>
<p>The governance that makes this safe for agents lives there too: the <code>strong</code> route carries a token <strong>rate limit</strong> (a <code>localRateLimit</code> route policy) — the backstop against an agent loop that won't stop spending. agentgateway can also price every call in real dollars through a top-level <code>config.modelCatalog</code>, but its schema is nested and provider-scoped — the obvious flat form didn't load for us — so wire it from the <a href="https://agentgateway.dev/docs/" target="_blank" rel="noopener noreferrer">agentgateway docs</a> for your release rather than copying a snippet.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>ONE PIECE STAYS YOURS TO RUN</div><div class="admonitionContent_BuS1"><p>You can swap agentgateway's governance for a managed AI gateway if you prefer, but that only covers the governance half — not the inference-aware front door. The ext-proc call to the Endpoint Picker, which places each weak-path request on the right GPU pod, has no managed equivalent today, so that piece stays yours to run whichever gateway fronts it.</p></div></div>
<p>Smoke-test both paths before the router exists:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># strong → Azure OpenAI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">curl -s http://agentgateway.llm.svc.cluster.local:4000/strong/v1/chat/completions \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -H "Content-Type: application/json" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -d '{"model":"gpt-5.1","messages":[{"role":"user","content":"Say no in one word."}]}'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># weak → KAITO via the Endpoint Picker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># (same shape RouteLLM sends: it appends /v1/chat/completions to api_base)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">curl -s http://agentgateway.llm.svc.cluster.local:4000/weak/v1/chat/completions \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -H "Content-Type: application/json" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -d '{"model":"phi-4-mini-instruct","messages":[{"role":"user","content":"Say hi in one word."}]}'</span><br></span></code></pre></div></div>
<p>Both return an OpenAI-shaped completion — the <code>strong</code> call comes back stamped with the resolved Azure deployment, the <code>weak</code> call from the in-cluster model, and neither caller had to know where the model lived:</p>
<div class="language-jsonc codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsonc codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">// strong → Azure OpenAI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{ "model": "gpt-5.1-2025-11-13",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "choices": [{ "message": { "role": "assistant", "content": "No." } }],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "usage": { "total_tokens": 29 } }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">// weak → KAITO / vLLM</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{ "model": "phi-4-mini-instruct",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "choices": [{ "message": { "role": "assistant", "content": "Hello." } }] }</span><br></span></code></pre></div></div>
<p><strong>What this layer decided:</strong> how to authenticate, meter, guardrail, and forward — on <em>policy</em>, never on content. It dispatches whatever model name it's handed. Choosing that name is Layer 1's job.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="layer-1--the-semantic-decision-and-the-threshold-that-is-the-whole-ballgame">Layer 1 — The semantic decision, and the threshold that is the whole ballgame<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#layer-1--the-semantic-decision-and-the-threshold-that-is-the-whole-ballgame" class="hash-link" aria-label="Direct link to Layer 1 — The semantic decision, and the threshold that is the whole ballgame" title="Direct link to Layer 1 — The semantic decision, and the threshold that is the whole ballgame" translate="no">​</a></h2>
<p>Here's the only layer that reads the prompt — and the one decision that actually moves the bill. For an agent firing hundreds of calls per task, it's also the layer with the most leverage: every easy call it keeps off the frontier model is multiplied by the loop.</p>
<p>RouteLLM is a set of routers trained on human-preference data to predict, per prompt, whether a cheap model can match a strong model's answer. On the paper's own model pair, the featured <code>mf</code> (matrix-factorization) router reaches <strong>~95% of GPT-4's quality on MT-Bench while making just ~26% of the GPT-4 calls</strong> — cutting cost by up to ~85% versus sending everything to GPT-4, by escalating only the genuinely hard prompts.</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>WHERE IT STRAINS</div><div class="admonitionContent_BuS1"><p>Those figures are for the pair RouteLLM was trained on — not your strong/weak pair. The router's quality predictor is fit to a particular pairing, so whether ~26% escalation holds with phi-4-mini as your weak model is something you confirm against your own traffic. And the router isn't free: scoring every prompt is itself a paid <code>text-embedding-3-small</code> round-trip, so the cost win depends on the price gap between strong and weak dwarfing that per-call overhead. That's the reason for the calibrate-then-close-the-loop step below, not a footnote to it.</p></div></div>
<p>The composition is the elegant part: <strong>RouteLLM's strong and weak upstreams are just agentgateway.</strong> It makes the semantic call, picks a model, and forwards into the governed data plane — so the decision layer inherits all of Layer 2's auth, budgets, and tracing without re-implementing a line of it.</p>
<p>One wiring detail the reviewer in you should catch: agentgateway routes by <strong>path</strong> (<code>/strong</code>, <code>/weak</code>), but RouteLLM hands its choice to litellm as a <em>model name</em>. So point each model at the matching agentgateway path with a per-model <code>api_base</code> rather than a single base URL:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># inside the RouteLLM container — each model maps to its agentgateway route</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">python -m routellm.openai_server --routers mf \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --strong-model openai/gpt-5.1 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --weak-model   openai/phi-4-mini-instruct \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --config routellm-config.yaml   # sets api_base per model → /strong and /weak</span><br></span></code></pre></div></div>
<p>One more gotcha from running this against current models: RouteLLM sends <code>presence_penalty</code> / <code>frequency_penalty</code> by default, and <strong>litellm</strong> (which RouteLLM uses to reach each provider) raises <code>UnsupportedParamsError</code> when the target model doesn't accept them — newer frontier models like the gpt-5.x family don't. Set <code>litellm.drop_params=True</code> so litellm silently drops the unsupported params instead of failing the call.</p>
<p>Clients call RouteLLM with a model string that encodes both the router and the cost threshold:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">router-mf-0.11593</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        │   └── threshold: the strong/weak cutoff — calibrate it</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        └────── the matrix-factorization router</span><br></span></code></pre></div></div>
<p>That threshold is the ballgame. Set it too low and you escalate everything and pay frontier prices for "hello"; set it too high and quality craters on the hard steps that needed the strong model. Don't guess it — calibrate against a representative sample so a target fraction of <em>your</em> traffic escalates:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">python -m routellm.calibrate_threshold --routers mf --strong-model-pct 0.5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># → 0.11593: the threshold that sends ~50% of calls to "strong"</span><br></span></code></pre></div></div>
<p>Then close the loop with the <em>real</em> numbers. Run live agent traffic and read the actual strong/weak split off <strong>agentgateway's</strong> metrics — not RouteLLM's estimate — and, once you've wired the cost catalog, the dollars too; nudge the threshold until the cost/quality trade sits where you want it. The router proposes; your own traffic disposes.</p>
<p>One caveat the rate card can't see: <strong>prompt caching</strong>. A cached input token costs a fraction of an uncached one — Azure OpenAI discounts cache hits, and the weak path's vLLM prefix cache is the very thing the Endpoint Picker routes on. Both caches are per-model and per-prefix, so per-call routing works against them: bounce a session between models and each one's cache for that growing context goes cold, so the <em>marginal</em> cost of a strong call whose prefix is still warm is much lower than its sticker rate, and the savings from offloading it are smaller than the headline prices imply. This is another reason to measure billed cost rather than multiply tokens by a static rate — and, where caching dominates over difficulty, to route at session or task granularity instead of per call. Per-call routing on prompt difficulty is still the right default; just don't let a token-rate estimate convince you the split is cheaper or more expensive than your bill says.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>MANAGED ALTERNATIVE FOR THIS LAYER</div><div class="admonitionContent_BuS1"><p><strong>Microsoft Foundry</strong>'s model router is a single managed deployment that routes among models by query complexity and cost — the managed analogue to RouteLLM. We feature RouteLLM precisely because the explicit, calibratable threshold is the heart of this design and worth holding in your own hands. If you'd rather not operate a router, the Foundry model router fills the same slot.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="observability-with-managed-azure-monitoring">Observability with managed Azure monitoring<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#observability-with-managed-azure-monitoring" class="hash-link" aria-label="Direct link to Observability with managed Azure monitoring" title="Direct link to Observability with managed Azure monitoring" translate="no">​</a></h2>
<p>Layer 1 trades quality for cost, so this section closes one question: can you trust the routing numbers, and can you watch the same signals the Endpoint Picker routes on? The answer splits by source — <strong>GPU and latency come from vLLM; the strong/weak split (and, once you wire the cost catalog, dollars) comes from agentgateway</strong> — so a complete view scrapes both.</p>
<p>To get there, add two <code>PodMonitor</code>s to the Managed Prometheus you enabled at cluster creation: one selecting the KAITO pods for vLLM's metrics on port 5000, one for agentgateway's on port 15020. Then link <strong>Azure Managed Grafana</strong> to the workspace. (One gotcha: the managed add-on uses Azure's CRDs under the <strong><code>azmonitoring.coreos.com</code></strong> API group, not upstream <code>monitoring.coreos.com</code>, so change a community manifest's <code>apiVersion</code> to <code>azmonitoring.coreos.com/v1</code> or the add-on won't scrape it.)</p>
<p><img decoding="async" loading="lazy" alt="Grafana dashboard of live metrics from the running cluster, scraped by Azure Managed Prometheus into an Azure Monitor workspace. The top row, &amp;quot;Layer 1–2 · Routing &amp;amp; cost (agentgateway),&amp;quot; holds three panels: request rate by route (weak steady around 2.5 req/s, strong around 0.7), a cumulative donut roughly 70% weak and 30% strong, and gateway p95 latency by route (strong around 2.5s, weak near zero). The bottom section, &amp;quot;Layer 3 · Weak-path GPU serving (vLLM via KAITO),&amp;quot; shows KV-cache utilization and queue depth both flat near zero under this light load, plus vLLM token throughput around 20 tokens/sec. The routing and cost panels come from agentgateway&amp;#39;s metrics; the GPU-serving panels come from vLLM&amp;#39;s." src="https://blog.aks.azure.com/assets/images/llm-routing-on-aks-observability-6ef0dda7c9e716e6b390e095b3d139fe.png" width="1379" height="1038" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-full-path-one-call">The full path, one call<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#the-full-path-one-call" class="hash-link" aria-label="Direct link to The full path, one call" title="Direct link to The full path, one call" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># trivial step → router picks weak → agentgateway → Endpoint Picker → KAITO pod</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">curl -s http://routellm.llm.svc.cluster.local:6060/v1/chat/completions \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -d '{"model":"router-mf-0.11593","messages":[{"role":"user","content":"What is 2+2?"}]}'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># hard step → router picks strong → agentgateway → Azure OpenAI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">curl -s http://routellm.llm.svc.cluster.local:6060/v1/chat/completions \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -d '{"model":"router-mf-0.11593",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       "messages":[{"role":"user","content":"Derive the EM algorithm and explain why each step cannot decrease the log-likelihood."}]}'</span><br></span></code></pre></div></div>
<p>The <code>model</code> field in each response is the receipt — it tells you which way the router decided. Against the calibrated threshold <code>0.11593</code>, <code>"What is 2+2?"</code> scores a strong-win-rate of <code>0.09</code> (below the line → weak) while the EM-algorithm prompt scores <code>0.29</code> (above → strong), so they come back from different models:</p>
<div class="language-jsonc codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsonc codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">// "What is 2+2?"             → "model": "phi-4-mini-instruct"   (weak, a fraction of a cent)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">// "Derive the EM algorithm…" → "model": "gpt-5.1-2025-11-13"    (strong, frontier pricing)</span><br></span></code></pre></div></div>
<p>Both hit the <em>same</em> endpoint with the <em>same</em> model string. Three decisions — content, policy, GPU state — resolved in one round trip, each by the layer that owns it. Now point your agent framework's base URL at that endpoint and every call in the loop gets sorted the same way.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="scale-it-to-your-problem">Scale it to your problem<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#scale-it-to-your-problem" class="hash-link" aria-label="Direct link to Scale it to your problem" title="Direct link to Scale it to your problem" translate="no">​</a></h2>
<p>You rarely need all of it on day one. Because the layers don't bleed into each other, you can adopt exactly the slice your problem demands:</p>
<ul>
<li><strong>One hosted model, a few agents, no self-hosting?</strong> <strong>Layer 2 only</strong> — agentgateway for auth, budgets, and guardrails over Azure OpenAI. There's nothing to route <em>to</em> and no GPUs to place on.</li>
<li><strong>Self-hosting at scale but one model class?</strong> <strong>KAITO + the Inference Extension</strong> give you managed serving and cache-aware placement. Skip semantic routing — there's only one model.</li>
<li><strong>A real cost problem from agents sending easy steps to expensive models?</strong> Add <strong>RouteLLM</strong>. It earns its keep the moment you have a meaningful price gap and a meaningful fraction of easy traffic — which describes essentially every agent in a loop.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://blog.aks.azure.com/2026/06/29/llm-routing-on-aks#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Routing agent traffic isn't one decision — it's three, each on its own signal: <strong>content</strong> picks the model (RouteLLM), <strong>policy</strong> governs the call (agentgateway), and <strong>GPU state</strong> picks the replica (the Inference Extension over KAITO-served vLLM). Keep those seams clean and you get a single OpenAI-compatible endpoint your agents point at, where every call is routed, metered, and traced by the layer that owns that concern — and where the easy calls an agent loop fires by the hundred quietly stay off the frontier bill.</p>
<p>From here, the obvious next moves all reinforce the same shape: autoscale the weak fleet on <code>vllm:num_requests_waiting</code> with the managed <strong>KEDA</strong> add-on so the GPU pool breathes with bursty agent traffic; lean into agentgateway's agent-native side by serving tools over <strong>MCP</strong> and routing <strong>agent-to-agent</strong> traffic through the same governed data plane; and add per-agent budgets at the gateway so spend stays attributable as more agents share the cluster.</p>
<p>The <a href="https://github.com/Azure/AKS/tree/master/examples/llm-routing-on-aks" target="_blank" rel="noopener noreferrer">example manifests</a> have everything here ready to adapt. For the components themselves, see <a href="https://learn.microsoft.com/azure/aks/ai-toolchain-operator" target="_blank" rel="noopener noreferrer">KAITO on AKS</a>, the <a href="https://gateway-api-inference-extension.sigs.k8s.io/" target="_blank" rel="noopener noreferrer">Gateway API Inference Extension</a>, <a href="https://agentgateway.dev/docs/" target="_blank" rel="noopener noreferrer">agentgateway</a>, <a href="https://github.com/lm-sys/RouteLLM" target="_blank" rel="noopener noreferrer">RouteLLM</a> (and its <a href="https://arxiv.org/abs/2406.18665" target="_blank" rel="noopener noreferrer">paper</a>), and <a href="https://learn.microsoft.com/azure/azure-monitor/containers/kubernetes-monitoring-enable" target="_blank" rel="noopener noreferrer">Managed Prometheus and Grafana for AKS</a>.</p>]]></content:encoded>
            <category>AI Inference</category>
            <category>Agent</category>
            <category>agentgateway</category>
            <category>RouteLLM</category>
            <category>KAITO</category>
            <category>GPU</category>
        </item>
        <item>
            <title><![CDATA[Deploy Anyscale on Azure with Terraform: a step-by-step guide]]></title>
            <link>https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform</link>
            <guid>https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform</guid>
            <pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Deploy Anyscale on Azure end-to-end with Terraform, AKS managed Gateway API, Istio-based app routing, and the AzAPI provider for newly released services.]]></description>
            <content:encoded><![CDATA[<p>A few weeks ago at Microsoft Build, the <a href="https://www.anyscale.com/blog/anyscale-on-azure-public-preview-build-and-deploy-ai-scale" target="_blank" rel="noopener noreferrer">public preview of Anyscale on Azure</a> was announced. If you're not familiar, <a href="https://learn.microsoft.com/azure/anyscale-on-azure/overview" target="_blank" rel="noopener noreferrer">Anyscale on Azure</a> is a managed platform for running distributed AI/ML workloads with <a href="https://www.ray.io/" target="_blank" rel="noopener noreferrer">Ray</a> on AKS. It's an Azure Native integration, a co-engineered effort between Anyscale and Microsoft, that deploys an operator onto your AKS cluster and integrates with Microsoft Entra ID for single sign-on (SSO).</p>
<p>The official <a href="https://learn.microsoft.com/azure/anyscale-on-azure/quickstart-azure-cli-gateway-envoy" target="_blank" rel="noopener noreferrer">quickstart</a> walks you through deploying Anyscale on Azure using the Azure CLI and Azure Portal with Envoy Gateway for ingress. That's a solid starting point, but if you're like me, you want everything in Terraform so your infrastructure is repeatable, version-controlled, and easy to tear down.</p>
<p>In this post, we'll walk through a Terraform configuration that deploys the full Anyscale on Azure stack, swapping out Envoy Gateway for <a href="https://learn.microsoft.com/azure/aks/app-routing-gateway-api" target="_blank" rel="noopener noreferrer">AKS managed Gateway API with Istio-based app routing</a>. With this approach, AKS handles the gateway lifecycle and you skip the step of installing and configuring Envoy Gateway entirely.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-were-building">What we're building<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#what-were-building" class="hash-link" aria-label="Direct link to What we're building" title="Direct link to What we're building" translate="no">​</a></h2>
<p>Before we dive into the code, here's a quick overview of what the Terraform configuration creates:</p>
<ul>
<li>A resource group to hold everything</li>
<li>An AKS cluster (Standard tier) with Gateway API and Istio-based app routing enabled</li>
<li>The Anyscale operator installed as an AKS cluster extension</li>
<li>A user-assigned managed identity with a federated credential for the Anyscale operator</li>
<li>An Azure Container Registry (Standard SKU) for container images</li>
<li>A storage account with hierarchical namespace (HNS) enabled and a private blob container</li>
<li>The Anyscale Cloud and Cloud Resource registered via the AzAPI provider (<code>Anyscale.Platform/clouds</code>)</li>
<li>Role assignments for storage, container registry, and AKS access</li>
</ul>
<p>The full Terraform code is available as a <a href="https://gist.github.com/pauldotyu/4226b4d735f1903c7cb6a0f3d35bc655" target="_blank" rel="noopener noreferrer">GitHub Gist</a>. In this post, I'll walk through each piece so you understand what's happening and why.</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_BuS1"><p>This walkthrough is designed to get you up and running quickly. It's a great way to understand the components that go into an Anyscale on Azure deployment and how they fit together, but it's not intended for production use as-is. For production environments, follow <a href="https://learn.microsoft.com/azure/cloud-adoption-framework/ready/landing-zone/" target="_blank" rel="noopener noreferrer">Azure landing zone architectures</a> and apply your organization's security and governance best practices around networking, identity, logging, and policy.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="prerequisites">Prerequisites<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h2>
<p>Before you get started, make sure you have the following:</p>
<ul>
<li><a href="https://learn.microsoft.com/cli/azure/install-azure-cli" target="_blank" rel="noopener noreferrer">Azure CLI</a> authenticated to a subscription</li>
<li><a href="https://developer.hashicorp.com/terraform/install" target="_blank" rel="noopener noreferrer">Terraform</a></li>
<li><a href="https://docs.anyscale.com/reference/quickstart-cli" target="_blank" rel="noopener noreferrer">Anyscale CLI</a> (installed via <a href="https://pipx.pypa.io/stable/" target="_blank" rel="noopener noreferrer">pipx</a>)</li>
<li><a href="https://jqlang.github.io/jq/download/" target="_blank" rel="noopener noreferrer">jq</a> for parsing Terraform outputs</li>
<li><a href="https://kubernetes.io/docs/tasks/tools/" target="_blank" rel="noopener noreferrer">kubectl</a> for interacting with the AKS cluster</li>
</ul>
<p>Your subscription also needs the following resource providers registered:</p>
<ul>
<li><code>Anyscale.Platform</code></li>
<li><code>Microsoft.ContainerService</code></li>
<li><code>Microsoft.ContainerRegistry</code></li>
<li><code>Microsoft.Storage</code></li>
<li><code>Microsoft.ManagedIdentity</code></li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>The AzureRM Terraform provider will automatically register most required resource providers during deployment. If your account lacks permission to register providers, register them manually with <code>az provider register --namespace &lt;provider-name&gt;</code> before running <code>terraform apply</code>.</p></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</div><div class="admonitionContent_BuS1"><p>Anyscale on Azure is currently in preview and available in select regions. See <a href="https://learn.microsoft.com/azure/anyscale-on-azure/supported-regions" target="_blank" rel="noopener noreferrer">supported regions</a> for the latest list.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-1-set-up-the-terraform-providers">Step 1: Set up the Terraform providers<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#step-1-set-up-the-terraform-providers" class="hash-link" aria-label="Direct link to Step 1: Set up the Terraform providers" title="Direct link to Step 1: Set up the Terraform providers" translate="no">​</a></h2>
<p>Create a new directory for your Terraform configuration and create a file named <code>main.tf</code>. For simplicity, we'll put everything in a single file so it's easy to follow along. In a real project, you'd probably want to split things up into separate files. See the <a href="https://developer.hashicorp.com/terraform/language/style#file-names" target="_blank" rel="noopener noreferrer">Terraform style guide on file names</a> for best practices.</p>
<p>We'll need a few providers: <code>azurerm</code> for Azure resources, <code>azapi</code> for the Anyscale Platform resources, <code>azuread</code> for the service principal lookup, and a couple of utility providers. Add the following to your <code>main.tf</code>:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  required_providers {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azapi = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      source  = "Azure/azapi"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      version = "~&gt; 2.10.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azuread = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      source  = "hashicorp/azuread"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      version = "~&gt; 3.8.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azurerm = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      source  = "hashicorp/azurerm"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      version = "~&gt; 4.76.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    local = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      source  = "hashicorp/local"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      version = "~&gt; 2.9.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    random = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      source  = "hashicorp/random"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      version = "~&gt; 3.9.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">provider "azurerm" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  features {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    resource_group {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      prevent_deletion_if_contains_resources = false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>We're using <code>azapi</code> here because the <code>Anyscale.Platform</code> resource type is brand new and not yet in the <code>azurerm</code> provider. The <code>azapi</code> provider lets us work with any ARM resource type directly, including preview ones. At the time of this writing, the <code>Anyscale.Platform</code> resource provider is not yet fully documented. I'll go into how I figured out the API schema for undocumented resources like this when we get to Step 5.</p>
<p>We also set <code>prevent_deletion_if_contains_resources = false</code> on the <code>azurerm</code> provider so that <code>terraform destroy</code> can cleanly remove the resource group without getting blocked by the resources inside it.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-2-create-the-foundational-resources">Step 2: Create the foundational resources<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#step-2-create-the-foundational-resources" class="hash-link" aria-label="Direct link to Step 2: Create the foundational resources" title="Direct link to Step 2: Create the foundational resources" translate="no">​</a></h2>
<p>Let's start with the basics: a resource group, some random naming helpers, and the storage and container registry resources that Anyscale needs. Add the following to your <code>main.tf</code>:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">variable "location" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  description = "Azure region. Must support Anyscale on Azure."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type        = string</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  default     = "westus3"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  validation {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    condition     = contains(["westcentralus", "eastus", "eastus2", "westus2", "westus3", "southcentralus"], var.location)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    error_message = "Must be a region that supports Anyscale on Azure."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">data "azurerm_client_config" "current" {}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "random_integer" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  min = 10</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  max = 99</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "random_string" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  length  = 4</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  special = false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  upper   = false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  lower   = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  numeric = false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">locals {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  random_name = "anyscale${random_integer.example.result}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_resource_group" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name     = "rg-${local.random_name}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  location = var.location</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>The <code>location</code> variable has a validation block that restricts you to regions where Anyscale on Azure is currently supported. This saves you from potential deployment failures later.</p>
<p>Next, add the container registry and storage account:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_container_registry" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name                = "${local.random_name}${random_string.example.result}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resource_group_name = azurerm_resource_group.example.name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  location            = azurerm_resource_group.example.location</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  sku                 = "Standard"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  admin_enabled       = false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  tags = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "anyscale-cloud" = local.random_name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_storage_account" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name                            = "${local.random_name}${random_string.example.result}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resource_group_name             = azurerm_resource_group.example.name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  location                        = azurerm_resource_group.example.location</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  account_kind                    = "StorageV2"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  account_tier                    = "Standard"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  account_replication_type        = "LRS"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  access_tier                     = "Hot"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  min_tls_version                 = "TLS1_2"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  https_traffic_only_enabled      = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  is_hns_enabled                  = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  default_to_oauth_authentication = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  shared_access_key_enabled       = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  blob_properties {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    cors_rule {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      allowed_headers    = ["*"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      allowed_methods    = ["DELETE", "GET", "HEAD", "POST", "PUT"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      allowed_origins    = ["https://*.anyscale.com"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      exposed_headers    = ["*"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      max_age_in_seconds = 3600</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  tags = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "anyscale-cloud" = local.random_name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_storage_container" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name                  = "anyscale-data"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  storage_account_id    = azurerm_storage_account.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  container_access_type = "private"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>A few things to note here. The storage account has <code>is_hns_enabled = true</code> because Anyscale uses Azure Data Lake Storage Gen2 (ADLS Gen2) for its cloud storage. HNS gives you hierarchical file system semantics on top of blob storage, which is what the <code>abfss://</code> URI scheme expects. The CORS rule allows the Anyscale web console to interact with the storage account directly from the browser.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-3-create-the-aks-cluster-with-gateway-api">Step 3: Create the AKS cluster with Gateway API<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#step-3-create-the-aks-cluster-with-gateway-api" class="hash-link" aria-label="Direct link to Step 3: Create the AKS cluster with Gateway API" title="Direct link to Step 3: Create the AKS cluster with Gateway API" translate="no">​</a></h2>
<p>This is where things get interesting. Instead of using the <code>azurerm_kubernetes_cluster</code> resource, we're using <code>azapi_resource</code> to create the AKS cluster. The managed Gateway API and Istio-based app routing features also <a href="https://learn.microsoft.com/azure/aks/app-routing-gateway-api" target="_blank" rel="noopener noreferrer">went GA recently</a>, but the ability to configure these features hasn't landed in the <code>azurerm</code> provider yet. So we use <code>azapi</code> to hit the AKS API directly. Add the following to your <code>main.tf</code>:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azapi_resource" "aks" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type      = "Microsoft.ContainerService/managedClusters@2026-03-02-preview"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  parent_id = azurerm_resource_group.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  location  = azurerm_resource_group.example.location</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name      = "aks-${local.random_name}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  schema_validation_enabled = false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  body = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    identity = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      type = "SystemAssigned"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    properties = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      dnsPrefix = "aks-${local.random_name}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      agentPoolProfiles = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          name              = "systempool"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          mode              = "System"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          enableAutoScaling = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          minCount          = 3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          maxCount          = 6</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      ingressProfile = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        gatewayAPI = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          installation = "Standard"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        webAppRouting = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          gatewayAPIImplementations = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            appRoutingIstio = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              mode = "Enabled"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      oidcIssuerProfile = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        enabled = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      securityProfile = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        workloadIdentity = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          enabled = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    sku = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      name = "Base"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      tier = "Standard"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  response_export_values = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "properties.oidcIssuerProfile.issuerURL",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "properties.identityProfile.kubeletidentity.objectId"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Let's break down the key parts of the <code>ingressProfile</code>:</p>
<ul>
<li><strong><code>gatewayAPI.installation = "Standard"</code></strong> installs the Gateway API CRDs and controller on the cluster</li>
<li><strong><code>webAppRouting.gatewayAPIImplementations.appRoutingIstio.mode = "Enabled"</code></strong> enables the Istio-based implementation, which gives you the <code>approuting-istio</code> gateway class</li>
</ul>
<p>We also enable OIDC issuer and workload identity on the cluster. These are required for the Anyscale operator to authenticate with Azure services using federated credentials instead of storing secrets.</p>
<p>The <code>response_export_values</code> block tells <code>azapi</code> to capture the OIDC issuer URL and the kubelet identity object ID from the cluster response, which we'll need for the federated credential and role assignments later.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-4-set-up-identity-and-role-assignments">Step 4: Set up identity and role assignments<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#step-4-set-up-identity-and-role-assignments" class="hash-link" aria-label="Direct link to Step 4: Set up identity and role assignments" title="Direct link to Step 4: Set up identity and role assignments" translate="no">​</a></h2>
<p>The Anyscale operator needs a managed identity with specific permissions to access resources on your behalf. We'll create a user-assigned managed identity, set up a federated credential so it can authenticate from within the cluster, and assign the necessary roles. Add the following to your <code>main.tf</code>:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azuread_service_principal" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  client_id    = "086bc555-6989-4362-ba30-fded273e432b" # Anyscale Kubernetes Operator Auth</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  use_existing = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_user_assigned_identity" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  location            = azurerm_resource_group.example.location</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name                = "${local.random_name}-operator-identity"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resource_group_name = azurerm_resource_group.example.name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  tags = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "anyscale-cloud" = local.random_name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_federated_identity_credential" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name                      = azurerm_user_assigned_identity.example.name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  audience                  = ["api://AzureADTokenExchange"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  issuer                    = azapi_resource.aks.output.properties.oidcIssuerProfile.issuerURL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  user_assigned_identity_id = azurerm_user_assigned_identity.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  subject                   = "system:serviceaccount:anyscale-operator:anyscale-operator"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>The <code>azuread_service_principal</code> resource references the well-known Anyscale Kubernetes Operator Auth application in your tenant. The <code>use_existing = true</code> flag tells Terraform to look up the existing service principal rather than trying to create one. This is needed so the Anyscale operator can authenticate against your cluster.</p>
<p>The federated identity credential is the glue between the Kubernetes service account (<code>anyscale-operator</code> in the <code>anyscale-operator</code> namespace) and the Azure managed identity. When the operator pod starts, it can exchange its Kubernetes service account token for an Azure access token without needing any stored secrets.</p>
<p>Now add the role assignments:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_role_assignment" "acr_pull" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  principal_id                     = azapi_resource.aks.output.properties.identityProfile.kubeletidentity.objectId</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  role_definition_name             = "AcrPull"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  scope                            = azurerm_container_registry.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  skip_service_principal_aad_check = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_role_assignment" "blob_data_owner" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  principal_id                     = azurerm_user_assigned_identity.example.principal_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  role_definition_name             = "Storage Blob Data Owner"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  scope                            = azurerm_storage_account.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  skip_service_principal_aad_check = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_role_assignment" "acr_push" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  principal_id                     = azurerm_user_assigned_identity.example.principal_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  role_definition_name             = "AcrPush"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  scope                            = azurerm_container_registry.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  skip_service_principal_aad_check = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_role_assignment" "acr_task_contributor" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  principal_id                     = azurerm_user_assigned_identity.example.principal_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  role_definition_name             = "Container Registry Tasks Contributor"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  scope                            = azurerm_container_registry.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  skip_service_principal_aad_check = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Here's what each role does:</p>
<ul>
<li><strong>AcrPull</strong> for the kubelet identity so the cluster can pull container images</li>
<li><strong>Storage Blob Data Owner</strong> for the operator identity to read/write data in the storage account</li>
<li><strong>AcrPush</strong> and <strong>Container Registry Tasks Contributor</strong> for the operator identity to push images and run ACR tasks</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-5-register-the-anyscale-cloud-resources">Step 5: Register the Anyscale Cloud resources<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#step-5-register-the-anyscale-cloud-resources" class="hash-link" aria-label="Direct link to Step 5: Register the Anyscale Cloud resources" title="Direct link to Step 5: Register the Anyscale Cloud resources" translate="no">​</a></h2>
<p>Now we register the Anyscale Cloud and Cloud Resource using the <code>Anyscale.Platform</code> resource provider through <code>azapi</code>. These resources tell the Anyscale control plane about your Azure environment.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>You might be wondering how I figured out the resource type, API version, and payload structure for <code>Anyscale.Platform</code>. At the time of writing, this resource provider isn't documented in the <a href="https://learn.microsoft.com/azure/templates/" target="_blank" rel="noopener noreferrer">Azure resource reference</a>. That's pretty common with newly released services. The trick is to go through the quickstart workflow in the Azure portal, complete the setup wizard, and then export the ARM template from the resource group. The exported template shows you exactly what resource provider, API version, and properties ARM uses under the hood. From there, you can translate it into <code>azapi_resource</code> blocks in Terraform.</p><p>This is a useful technique to keep in your back pocket. Anytime a new Azure service launches and you want to automate it with Terraform before the <code>azurerm</code> provider catches up, the portal's template export gives you the schema you need.</p></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>danger</div><div class="admonitionContent_BuS1"><p>Since Anyscale on Azure is in public preview, the <code>Anyscale.Platform</code> API is using a preview version (<code>2026-02-01-preview</code>). There's a good chance the API shape will change as the service moves toward general availability. If you're building on top of this, be prepared to update your Terraform configuration when new API versions are released.</p></div></div>
<p>Add the following to your <code>main.tf</code>:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azapi_resource" "anyscale_cloud" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type      = "Anyscale.Platform/clouds@2026-02-01-preview"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  parent_id = azurerm_resource_group.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  location  = azurerm_resource_group.example.location</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name      = local.random_name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  schema_validation_enabled = false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  body = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    properties = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      acrResourceId = azurerm_container_registry.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  tags = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "anyscale-cloud" = local.random_name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  response_export_values = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "properties.ssoUrl",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "properties.cloudResourceId"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  depends_on = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azapi_resource.aks</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "azapi_resource" "anyscale_cloud_resource" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type      = "Anyscale.Platform/clouds/cloudResources@2026-02-01-preview"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  parent_id = azapi_resource.anyscale_cloud.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  location  = azurerm_resource_group.example.location</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name      = "default"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  schema_validation_enabled = false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  body = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    properties = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      provider                    = "Azure"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      computeStack                = "K8S"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      cloudStorageBucketEndpoint  = azurerm_storage_account.example.primary_blob_endpoint</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      cloudStorageBucketName      = "abfss://${azurerm_storage_container.example.name}@${azurerm_storage_account.example.primary_dfs_host}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      anyscaleOperatorIamIdentity = azurerm_user_assigned_identity.example.principal_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  tags = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "anyscale-cloud" = local.random_name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  response_export_values = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "properties.cloudResourceId"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  depends_on = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azapi_resource.aks</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>The <code>Anyscale.Platform/clouds</code> resource registers a new Anyscale "cloud" (their term for a connected infrastructure environment) and associates it with your container registry. The child <code>cloudResources</code> resource provides the details about compute (Kubernetes), storage (the ADLS Gen2 bucket), and the operator identity.</p>
<p>Notice the <code>cloudStorageBucketName</code> uses the <code>abfss://</code> URI format. This is the Azure Blob File System (ABFS) scheme that ADLS Gen2 uses, and it's how Anyscale accesses your storage.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-6-install-the-anyscale-operator-extension">Step 6: Install the Anyscale operator extension<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#step-6-install-the-anyscale-operator-extension" class="hash-link" aria-label="Direct link to Step 6: Install the Anyscale operator extension" title="Direct link to Step 6: Install the Anyscale operator extension" translate="no">​</a></h2>
<p>The Anyscale operator is deployed as an AKS cluster extension. This is where we wire up the gateway configuration. Add the following to your <code>main.tf</code>:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_kubernetes_cluster_extension" "anyscale_operator" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name           = "anyscaleoperator"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  cluster_id     = azapi_resource.aks.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  extension_type = "Anyscale.AKS.Operator"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  release_train  = "stable"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  plan {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name      = "anyscale-operator"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    product   = "anyscale-operator-aks"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    publisher = "anyscale1750870039553"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  configuration_settings = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "global.auth.audience"          = "api://086bc555-6989-4362-ba30-fded273e432b/.default"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "global.auth.iamIdentity"       = azurerm_user_assigned_identity.example.client_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "global.cloudDeploymentId"      = azapi_resource.anyscale_cloud_resource.output.properties.cloudResourceId</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "global.controlPlaneURL"        = "https://console.azure.anyscale.com"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "workloads.serviceAccount.name" = "anyscale-operator"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "networking.gateway.enabled"    = "true"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "networking.gateway.name"       = "gateway"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "networking.gateway.className"  = "approuting-istio"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "networking.gateway.namespace"  = "anyscale-operator"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "networking.gateway.apiVersion" = "gateway.networking.k8s.io/v1"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "networking.gateway.hostname"   = "${replace(azapi_resource.anyscale_cloud_resource.output.properties.cloudResourceId, "_", "-")}.${azapi_resource.aks.location}.cloudapp.azure.com"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  depends_on = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azurerm_federated_identity_credential.example</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>The key configuration settings to pay attention to:</p>
<ul>
<li><strong><code>networking.gateway.className = "approuting-istio"</code></strong> tells the operator to use the AKS managed Istio gateway class</li>
<li><strong><code>networking.gateway.apiVersion = "gateway.networking.k8s.io/v1"</code></strong> ensures we're using the GA Gateway API spec</li>
<li><strong><code>networking.gateway.hostname</code></strong> constructs a DNS label that Azure will use to assign a public FQDN to the gateway's load balancer</li>
</ul>
<p>The <code>plan</code> block references the Azure Marketplace listing for the Anyscale operator. This is how Azure knows what extension to install and which publisher it comes from.</p>
<p>We also need a role assignment so you can manage the Anyscale platform resources. Add this to your <code>main.tf</code>:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_role_assignment" "anyscale_platform_contributor" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  principal_id         = data.azurerm_client_config.current.object_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  role_definition_name = "Anyscale Platform Contributor Role"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  scope                = azapi_resource.anyscale_cloud.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-7-outputs-and-the-gateway-template">Step 7: Outputs and the Gateway template<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#step-7-outputs-and-the-gateway-template" class="hash-link" aria-label="Direct link to Step 7: Outputs and the Gateway template" title="Direct link to Step 7: Outputs and the Gateway template" translate="no">​</a></h2>
<p>Before we deploy, we need two more things: Terraform output values so we can grab resource names after the deployment, and a template file for the Gateway manifest.</p>
<p>First, add the <code>local_file</code> resource to your <code>main.tf</code>. It takes a template (which we'll create next) and renders it into <code>anyscale-gateway.yaml</code> with the actual cloud resource ID:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "local_file" "anyscale_gateway" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  filename = "anyscale-gateway.yaml"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  content = templatefile("anyscale-gateway.tmpl",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      ANYSCALE_CLOUD_RESOURCE_ID = replace(azapi_resource.anyscale_cloud_resource.output.properties.cloudResourceId, "_", "-")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  )</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Next, add the following outputs to your <code>main.tf</code>:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">output "rg_name" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  value = azurerm_resource_group.example.name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">output "aks_name" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  value = azapi_resource.aks.name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">output "anyscale_cloud_id" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  value = split("/", azapi_resource.anyscale_cloud.output.properties.ssoUrl)[4]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">output "anyscale_cloud_resource_id" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  value = azapi_resource.anyscale_cloud_resource.output.properties.cloudResourceId</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Finally, create a file named <code>anyscale-gateway.tmpl</code> in the same directory. Terraform uses this template to generate the Gateway manifest with your cloud resource ID injected:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> anyscale</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">operator</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">gatewayClassName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> approuting</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">istio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">infrastructure</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">annotations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">service.beta.kubernetes.io/azure-dns-label-name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">ANYSCALE_CLOUD_RESOURCE_ID</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">listeners</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">protocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> HTTP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">allowedRoutes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">namespaces</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">from</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Same</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">443</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">protocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> HTTPS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">hostname</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"*.i.azure.anyscaleuserdata.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">tls</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Terminate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">certificateRefs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> anyscale</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">ANYSCALE_CLOUD_RESOURCE_ID</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">certificate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">allowedRoutes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">namespaces</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">from</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Same</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">session</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">443</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">protocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> HTTPS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">hostname</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"*.s.azure.anyscaleuserdata.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">tls</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Terminate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">certificateRefs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> anyscale</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">ANYSCALE_CLOUD_RESOURCE_ID</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">certificate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">allowedRoutes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">namespaces</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">from</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Same</span><br></span></code></pre></div></div>
<p>The Gateway defines three listeners: one for HTTP traffic and two for HTTPS. The HTTPS listeners handle Anyscale's interactive (<code>*.i.</code>) and session (<code>*.s.</code>) subdomains with TLS termination. The <code>azure-dns-label-name</code> annotation tells Azure to assign a public DNS label to the gateway's load balancer IP.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="deploy-it">Deploy it<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#deploy-it" class="hash-link" aria-label="Direct link to Deploy it" title="Direct link to Deploy it" translate="no">​</a></h2>
<p>With all the Terraform code in place, it's time to deploy. Sign in to Azure and set your subscription:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az login</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az account set -s &lt;subscription-id&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv)</span><br></span></code></pre></div></div>
<p>Initialize Terraform to download the providers:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform init</span><br></span></code></pre></div></div>
<p>Then format and validate to make sure everything is clean:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform fmt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">terraform validate</span><br></span></code></pre></div></div>
<p>If validation passes, go ahead and apply:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform apply</span><br></span></code></pre></div></div>
<p>Review the plan and type <code>yes</code> to proceed. The deployment takes about 7-10 minutes since it's creating an AKS cluster, installing the Anyscale operator extension, and registering the cloud resources.</p>
<p>Once it's done, grab the outputs and connect to the cluster:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">read -r \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  RG_NAME \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  AKS_NAME \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ANYSCALE_CLOUD_ID \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ANYSCALE_CLOUD_RESOURCE_ID &lt;&lt;&lt; "$(terraform output -json | jq -r \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    '[.rg_name.value,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      .aks_name.value,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      .anyscale_cloud_id.value,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      .anyscale_cloud_resource_id.value] | @tsv')"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az aks get-credentials -g $RG_NAME -n $AKS_NAME</span><br></span></code></pre></div></div>
<p>Then deploy the gateway:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f anyscale-gateway.yaml</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="verify-the-deployment">Verify the deployment<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#verify-the-deployment" class="hash-link" aria-label="Direct link to Verify the deployment" title="Direct link to Verify the deployment" translate="no">​</a></h2>
<p>Check that the Anyscale operator and gateway-related pods are running:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get po -A</span><br></span></code></pre></div></div>
<p>You should see pods in the <code>anyscale-operator</code> namespace for the operator itself, and Istio-related pods in the gateway infrastructure namespaces.</p>
<p>Now sign in to the Anyscale console. I installed the Anyscale CLI using <a href="https://pipx.pypa.io/stable/" target="_blank" rel="noopener noreferrer">pipx</a>, which I like because it installs Python CLI tools in isolated environments, similar to how <code>npx</code> works in the Node.js world. You get the tool without polluting your global Python packages.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pipx install anyscale</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export ANYSCALE_HOST=https://console.azure.anyscale.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">anyscale login</span><br></span></code></pre></div></div>
<p>Verify the cloud is registered and healthy:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">anyscale cloud list</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">anyscale cloud verify --id $ANYSCALE_CLOUD_ID</span><br></span></code></pre></div></div>
<p>The <code>cloud list</code> command should show your cloud with the <code>AZURE</code> provider and your region. The <code>cloud verify</code> command runs a series of checks against your cluster, including operator health, identity, and networking. You want to see all checks come back as <code>PASSED</code>.</p>
<p>From here, you can follow the <a href="https://learn.microsoft.com/azure/anyscale-on-azure/quickstart-azure-cli-gateway-envoy#run-your-first-workload" target="_blank" rel="noopener noreferrer">quickstart guide to run your first workload</a>. You can also open the Anyscale console directly from the Azure portal by navigating to your Anyscale resource and selecting the console link. That's where you can explore workspaces, submit jobs, and manage your Ray clusters.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="cleanup">Cleanup<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#cleanup" class="hash-link" aria-label="Direct link to Cleanup" title="Direct link to Cleanup" translate="no">​</a></h2>
<p>When you're done experimenting, tear everything down:</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>danger</div><div class="admonitionContent_BuS1"><p>If you have any running workspaces, make sure to terminate them first. You can do this from the Anyscale console UI or via the CLI:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">anyscale workspace_v2 terminate --project default -n &lt;workspace-name&gt;</span><br></span></code></pre></div></div><p>During the preview, if the Azure infrastructure is removed while workspaces are still running, those workspaces can end up in a state where they can't fully terminate, which prevents the Anyscale cloud from being deleted. This is expected to improve as the service works towards general availability.</p></div></div>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform destroy</span><br></span></code></pre></div></div>
<p>Review the plan and type <code>yes</code> to confirm. This will remove all the resources, including the AKS cluster, Anyscale cloud registration, storage, and container registry.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="summary">Summary<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<p>In this post, we walked through deploying Anyscale on Azure using Terraform with AKS managed Gateway API and Istio-based app routing. Along the way, we picked up a useful technique for figuring out API schemas when the docs haven't caught up yet, and saw how the <code>azapi</code> provider lets you automate new Azure services before the <code>azurerm</code> provider adds support.</p>
<p>The full Terraform code is available as a <a href="https://gist.github.com/pauldotyu/4226b4d735f1903c7cb6a0f3d35bc655" target="_blank" rel="noopener noreferrer">GitHub Gist</a>. Clone it, tweak the variables, and you should be up and running in under 10 minutes.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="whats-next">What's next?<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#whats-next" class="hash-link" aria-label="Direct link to What's next?" title="Direct link to What's next?" translate="no">​</a></h3>
<ul>
<li>Explore the <a href="https://learn.microsoft.com/azure/anyscale-on-azure/" target="_blank" rel="noopener noreferrer">Anyscale on Azure documentation</a> for more configuration options</li>
<li>Check out the <a href="https://docs.ray.io/" target="_blank" rel="noopener noreferrer">Ray documentation</a> for building distributed applications</li>
<li>Try scaling your Ray cluster with different node pool configurations and GPU-enabled VMs</li>
<li>Look into <a href="https://docs.anyscale.com/workspaces/get-started/" target="_blank" rel="noopener noreferrer">Anyscale workspaces</a> for interactive development on your cluster</li>
</ul>
<p>Have questions or run into issues? Drop us a note in <a href="https://github.com/Azure/AKS/discussions" target="_blank" rel="noopener noreferrer">GitHub Discussions</a> or open an <a href="https://github.com/Azure/AKS/issues/new" target="_blank" rel="noopener noreferrer">issue</a>.</p>
<p>Happy deploying!</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="resources">Resources<a href="https://blog.aks.azure.com/2026/06/15/get-anyscale-on-azure-up-and-running-with-terraform#resources" class="hash-link" aria-label="Direct link to Resources" title="Direct link to Resources" translate="no">​</a></h2>
<ul>
<li><a href="https://gist.github.com/pauldotyu/4226b4d735f1903c7cb6a0f3d35bc655" target="_blank" rel="noopener noreferrer">Full Terraform code for this walkthrough</a></li>
<li><a href="https://www.anyscale.com/blog/anyscale-on-azure-public-preview-build-and-deploy-ai-scale" target="_blank" rel="noopener noreferrer">Anyscale on Azure public preview announcement</a></li>
<li><a href="https://learn.microsoft.com/azure/anyscale-on-azure/overview" target="_blank" rel="noopener noreferrer">What is Anyscale on Azure?</a></li>
<li><a href="https://learn.microsoft.com/azure/anyscale-on-azure/quickstart-azure-cli-gateway-envoy" target="_blank" rel="noopener noreferrer">Quickstart: Deploy Anyscale on Azure</a></li>
<li><a href="https://learn.microsoft.com/azure/aks/gateway-api" target="_blank" rel="noopener noreferrer">AKS managed Gateway API</a></li>
<li><a href="https://learn.microsoft.com/azure/aks/app-routing-gateway-api" target="_blank" rel="noopener noreferrer">Istio-based app routing with Gateway API on AKS</a></li>
<li><a href="https://registry.terraform.io/providers/Azure/azapi/latest" target="_blank" rel="noopener noreferrer">AzAPI Terraform provider</a></li>
<li><a href="https://docs.ray.io/" target="_blank" rel="noopener noreferrer">Ray documentation</a></li>
</ul>]]></content:encoded>
            <category>Anyscale</category>
            <category>Ray</category>
            <category>App Routing</category>
            <category>Terraform</category>
        </item>
        <item>
            <title><![CDATA[App Routing Gateway API is GA: Here's a Demo]]></title>
            <link>https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga</link>
            <guid>https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga</guid>
            <pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[App Routing's Gateway API implementation is now GA, with built-in Azure DNS and Azure Key Vault TLS integration plus JSON access logs on gateway proxies.]]></description>
            <content:encoded><![CDATA[<p>The AKS App Routing add-on's Kubernetes Gateway API implementation — <code>approuting-istio</code> — is <strong>generally available</strong>. Together with that, the <strong>Managed Gateway API installation</strong> for AKS is also GA, so the CRDs, the controller stack, and the gateway data plane you need to run Gateway API on AKS are all now first-class, supported features.</p>
<p><img decoding="async" loading="lazy" alt="Cartoon with Key Vault, Istio, and DNS" src="https://blog.aks.azure.com/assets/images/Designer-f63cf63294484f54d29f19ec716bd2f8.jpg" width="1536" height="1024" class="img_ev3q"></p>
<p>Since <a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api">our March preview announcement</a>, we've also shipped two of our most requested capabilities that gated a real production story for Gateway API on AKS:</p>
<ul>
<li><strong>Azure DNS and Azure Key Vault, wired in for you.</strong> No more manually deploying a <code>SecretProviderClass</code>, a sync pod, or a separate <code>external-dns</code> instance just to get a TLS-terminated, DNS-resolvable hostname. Drop two <code>tls.options</code> on a listener, apply an <code>ExternalDNS</code> CR, and you're done.</li>
<li><strong>Access logs, enabled out of the box.</strong> Every gateway proxy writes a structured JSON access log line per request to stdout. <code>kubectl logs</code> the gateway deployment and you're already debugging — no <code>Telemetry</code> resource, no <code>EnvoyFilter</code>, no opt-in flag. This closes one of the last parity gaps with the nginx experience our users are accustomed to.</li>
</ul>
<p>The rest of this post walks through both. We'll create a cluster, expose a sample app over the Gateway API, see access logs land on stdout, and then layer on DNS + TLS using the <code>ClusterExternalDNS</code> and <code>ExternalDNS</code> CRDs plus listener TLS options. A working DNS zone and Key Vault are part of the demo, so by the end you'll have HTTPS records resolving in a zone you own.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Rollout</div><div class="admonitionContent_BuS1"><p><a href="https://learn.microsoft.com/azure/aks/app-routing-gateway-api" target="_blank" rel="noopener noreferrer">App Routing Istio</a> and the <a href="https://learn.microsoft.com/azure/aks/managed-gateway-api" target="_blank" rel="noopener noreferrer">Managed Gateway API installation</a> both went GA as part of the <strong>AKS 20260428 release</strong>, which has rolled out to all regions — those features are available everywhere today. The DNS/TLS operator integration and access-logging-by-default ship with the <strong>AKS 20260529 release</strong>; track region-by-region progress on the <a href="https://releases.aks.azure.com/AKSRelease" target="_blank" rel="noopener noreferrer">AKS release tracker</a>. Even following the release rolling out to your region, there may be some delays (on the order of a couple of days) that may prevent you from using it. In this case, it's best to use a region that AKS rolled out to earlier.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="whats-new">What's new<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#whats-new" class="hash-link" aria-label="Direct link to What's new" title="Direct link to What's new" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="access-logs-by-default">Access logs by default<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#access-logs-by-default" class="hash-link" aria-label="Direct link to Access logs by default" title="Direct link to Access logs by default" translate="no">​</a></h3>
<p>Every Envoy gateway proxy that AKS provisions for your <code>Gateway</code> resources now writes one structured JSON access log line per request to its container log out of the box. The very first thing you can do once traffic flows is <code>kubectl logs</code> the gateway deployment and watch requests arrive. No Istio resources needed to manage it, and no opt-in flag. This brings the Gateway API experience closer to parity with the nginx-based experience our users are accustomed to.</p>
<p>Because the logs land on stdout, they flow through the standard container log pipeline, so you can collect them centrally instead of tailing each gateway by hand. Enable the <strong>Azure Monitor (Container Insights) add-on</strong>, and each gateway proxy's access logs are collected into a <a href="https://learn.microsoft.com/azure/azure-monitor/logs/log-analytics-workspace-overview" target="_blank" rel="noopener noreferrer">Log Analytics workspace</a>, where you can query, retain, and alert on them with KQL. You can enable it when you create or update the cluster (<code>az aks create/update --enable-addons monitoring --workspace-resource-id &lt;workspace-id&gt;</code>), or onboard an existing cluster through the portal — see <a href="https://learn.microsoft.com/azure/azure-monitor/containers/kubernetes-monitoring-enable" target="_blank" rel="noopener noreferrer">Enable monitoring for Kubernetes clusters</a> and the <a href="https://learn.microsoft.com/azure/aks/monitor-aks" target="_blank" rel="noopener noreferrer">Monitor AKS</a> overview. Once it's enabled, the per-request access log lines show up in the <a href="https://learn.microsoft.com/azure/azure-monitor/containers/container-insights-logs-schema" target="_blank" rel="noopener noreferrer"><code>ContainerLogV2</code> table</a>, so a query like <code>ContainerLogV2 | where PodName startswith "httpbin-gateway-approuting-istio"</code> gives you every request the gateway served across all replicas.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="dns-and-tls-integration-via-the-app-routing-operator">DNS and TLS integration via the App Routing operator<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#dns-and-tls-integration-via-the-app-routing-operator" class="hash-link" aria-label="Direct link to DNS and TLS integration via the App Routing operator" title="Direct link to DNS and TLS integration via the App Routing operator" translate="no">​</a></h3>
<p>At preview, DNS and TLS automation was a major piece of the ingress-nginx experience the Gateway API path didn't match. That gap is now closed via two paths in the App Routing operator:</p>
<ul>
<li><strong>TLS</strong> — A pair of listener <code>tls.options</code> keys (<code>kubernetes.azure.com/tls-cert-keyvault-uri</code> and <code>kubernetes.azure.com/tls-cert-service-account</code>) tell the operator to provision a <code>SecretProviderClass</code>, sync the certificate from Azure Key Vault as a <code>kubernetes.io/tls</code> Secret, and patch the listener's <code>certificateRefs</code> for you.</li>
<li><strong>DNS</strong> — Two CRDs, <code>ClusterExternalDNS</code> (cluster-scoped) and <code>ExternalDNS</code> (namespace-scoped), let you stand up an <code>external-dns</code> instance scoped to one or more Azure DNS zones and tell it to watch <code>Gateway</code>/<code>HTTPRoute</code>/<code>GRPCRoute</code> resources. Records appear automatically based on the hostnames on your routes.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="demo-from-cluster-create-to-https-with-dns">Demo: from cluster create to HTTPS-with-DNS<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#demo-from-cluster-create-to-https-with-dns" class="hash-link" aria-label="Direct link to Demo: from cluster create to HTTPS-with-DNS" title="Direct link to Demo: from cluster create to HTTPS-with-DNS" translate="no">​</a></h2>
<p>The rest of this post is a hands-on walkthrough that takes you through our newly-GA'd features along with the enhancements we're so excited about. To follow along you'll need:</p>
<ul>
<li>Azure CLI 2.86.0 or later (<code>az --version</code> to check, <code>az upgrade</code> to update).</li>
<li>An Azure subscription and resource group, plus permission to create resources in it.</li>
<li>Sufficient Key Vault RBAC on your own Azure identity to create certificates — <code>Key Vault Certificates Officer</code> is the minimum, <code>Key Vault Administrator</code> is fine. Grant this on the vault you'll create in step 5b before running the cert-creation command.</li>
</ul>
<p>Export these once and reuse them throughout:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export RESOURCE_GROUP=&lt;your-resource-group&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export CLUSTER=&lt;your-cluster-name&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export LOCATION=&lt;your-azure-region&gt;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-create-the-cluster-or-update-an-existing-one">1. Create the cluster (or update an existing one)<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#1-create-the-cluster-or-update-an-existing-one" class="hash-link" aria-label="Direct link to 1. Create the cluster (or update an existing one)" title="Direct link to 1. Create the cluster (or update an existing one)" translate="no">​</a></h3>
<p>The three flags that matter are <code>--enable-app-routing-istio</code> (turns on the App Routing Gateway API implementation), <code>--enable-gateway-api</code> (installs the AKS-managed Gateway API CRDs), and <code>--enable-app-routing</code> (deploys the App Routing operator, which manages the DNS/TLS integration). All APIs are GA — no preview-feature registration and no <code>aks-preview</code> extension required.</p>
<p><strong>On a new cluster:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az aks create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $RESOURCE_GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name $CLUSTER \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --location $LOCATION \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-app-routing-istio \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-app-routing \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-gateway-api \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-oidc-issuer \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-workload-identity \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-addons azure-keyvault-secrets-provider</span><br></span></code></pre></div></div>
<p><strong>On an existing cluster:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az aks update \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $RESOURCE_GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name $CLUSTER \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-app-routing \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-app-routing-istio \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-gateway-api \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-oidc-issuer \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-workload-identity</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># CSI addon needs its own command on existing clusters</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az aks enable-addons \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $RESOURCE_GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name $CLUSTER \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --addons azure-keyvault-secrets-provider</span><br></span></code></pre></div></div>
<p><code>--enable-oidc-issuer</code> and <code>--enable-workload-identity</code> are required for the DNS/TLS demo later — both DNS and TLS use Microsoft Entra Workload Identity to authenticate to Azure DNS and Key Vault. The <code>azure-keyvault-secrets-provider</code> addon installs the Secrets Store CSI Driver, which the operator uses to materialize Key Vault certificates as Kubernetes Secrets. Set them all now even if you don't have a workload to expose yet.</p>
<p>Pull credentials so <code>kubectl</code> works:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-confirm-the-control-plane-is-up">2. Confirm the control plane is up<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#2-confirm-the-control-plane-is-up" class="hash-link" aria-label="Direct link to 2. Confirm the control plane is up" title="Direct link to 2. Confirm the control plane is up" translate="no">​</a></h3>
<p>The App Routing Gateway API implementation runs <code>istiod</code> in the <code>aks-istio-system</code> namespace.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n aks-istio-system</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME                      READY   STATUS    RESTARTS   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">istiod-54b4ff45cf-htph8   1/1     Running   0          3m15s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">istiod-54b4ff45cf-wlvgd   1/1     Running   0          3m</span><br></span></code></pre></div></div>
<p>You'll also see the <code>app-routing-system</code> namespace with NGINX still running:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n app-routing-system</span><br></span></code></pre></div></div>
<p>You can ignore this for the rest of the demo. NGINX-by-default is on its way out as part of the <a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#why-now-the-ingress-nginx-retirement">Ingress-NGINX retirement</a>. Once we stop deploying it after November 2026, it won't appear for users by default.</p>
<p>The <code>approuting-istio</code> <code>GatewayClass</code> is what you'll target with your <code>Gateway</code> resources:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get gatewayclass approuting-istio</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME               CONTROLLER                    ACCEPTED   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">approuting-istio   istio.aks.azure.com/gateway-controller   True       4m</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="3-expose-a-sample-app-via-gateway-api">3. Expose a sample app via Gateway API<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#3-expose-a-sample-app-via-gateway-api" class="hash-link" aria-label="Direct link to 3. Expose a sample app via Gateway API" title="Direct link to 3. Expose a sample app via Gateway API" translate="no">​</a></h3>
<p>We'll deploy <code>httpbin</code>, front it with a single <code>Gateway</code> listener over plain HTTP, attach an <code>HTTPRoute</code>, and verify traffic flows. (We'll make this HTTPS in section 5.)</p>
<p>Deploy <code>httpbin</code> in the <code>default</code> namespace:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.27/samples/httpbin/httpbin.yaml</span><br></span></code></pre></div></div>
<p>Create the <code>Gateway</code> and <code>HTTPRoute</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f - &lt;&lt;EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: httpbin-gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  gatewayClassName: approuting-istio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  listeners:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - name: http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    port: 80</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    protocol: HTTP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    allowedRoutes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      namespaces:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        from: Same</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: HTTPRoute</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: httpbin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  parentRefs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - name: httpbin-gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  hostnames: ["httpbin.example.com"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  rules:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - matches:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - path:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        type: PathPrefix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        value: /get</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    backendRefs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - name: httpbin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      port: 8000</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<p>AKS provisions the Envoy <code>Deployment</code>, <code>Service</code>, <code>HorizontalPodAutoscaler</code>, and <code>PodDisruptionBudget</code> for the <code>Gateway</code> automatically. Wait for the Gateway to be programmed and grab its external IP:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl wait --for=condition=programmed gateways.gateway.networking.k8s.io httpbin-gateway --timeout=120s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export INGRESS_HOST=$(kubectl get gateways.gateway.networking.k8s.io httpbin-gateway -ojsonpath='{.status.addresses[0].value}')</span><br></span></code></pre></div></div>
<p>Send a request:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">curl -s -I -H "Host: httpbin.example.com" "http://$INGRESS_HOST/get"</span><br></span></code></pre></div></div>
<p>You should see an <code>HTTP/1.1 200 OK</code>.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="4-see-access-logs-on-the-gateway-proxy">4. See access logs on the gateway proxy<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#4-see-access-logs-on-the-gateway-proxy" class="hash-link" aria-label="Direct link to 4. See access logs on the gateway proxy" title="Direct link to 4. See access logs on the gateway proxy" translate="no">​</a></h3>
<p>Each <code>Gateway</code> resource is backed by a managed <code>Deployment</code> named <code>&lt;gateway-name&gt;-approuting-istio</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get deployment httpbin-gateway-approuting-istio</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME                               READY   UP-TO-DATE   AVAILABLE   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">httpbin-gateway-approuting-istio   2/2     2            2           1m</span><br></span></code></pre></div></div>
<p>Tail its logs and re-issue a curl:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs deploy/httpbin-gateway-approuting-istio --tail=20 -f</span><br></span></code></pre></div></div>
<p>You should see an access log for each of your requests. That's the JSON-formatted Envoy access log written to stdout, on by default — useful for sanity checks, post-mortem analysis, or shipping into Azure Monitor / Log Analytics via the standard container log pipeline.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="5-add-azure-dns-and-tls">5. Add Azure DNS and TLS<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#5-add-azure-dns-and-tls" class="hash-link" aria-label="Direct link to 5. Add Azure DNS and TLS" title="Direct link to 5. Add Azure DNS and TLS" translate="no">​</a></h3>
<p>Now for the best part (well, at least in my opinion). We'll create an Azure DNS zone we own, an Azure Key Vault with a real cert for a hostname in that zone, a user-assigned managed identity that can write to both, and then wire everything in via two CRDs and two listener <code>tls.options</code>.</p>
<p>For this demo we'll use the zone <code>ga-demo.dev</code> with two sub-hosts, <code>a.ga-demo.dev</code> and <code>b.ga-demo.dev</code>. Substitute your own zone name.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export ZONE_NAME=ga-demo.dev</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export KV_NAME=approuting-ga-demo-kv  # must be globally unique</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export UAMI_NAME=approuting-ga-demo-id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export SA_NAME=approuting-demo-sa</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="5a-create-an-azure-dns-zone">5a. Create an Azure DNS zone<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#5a-create-an-azure-dns-zone" class="hash-link" aria-label="Direct link to 5a. Create an Azure DNS zone" title="Direct link to 5a. Create an Azure DNS zone" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az network dns zone create --resource-group $RESOURCE_GROUP --name $ZONE_NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export ZONE_ID=$(az network dns zone show --resource-group $RESOURCE_GROUP --name $ZONE_NAME --query id -o tsv)</span><br></span></code></pre></div></div>
<p>The zone's authoritative nameservers are listed under <code>az network dns zone show ... --query nameServers</code>. You can find this on the Azure portal too. If you're using a registrar-owned domain in production, you'd point the registrar at those NS records. But for a one-off demo, you can resolve through the zone NS directly with <code>dig @&lt;ns&gt; a.${ZONE_NAME}</code> which is what we'll do later to ensure our curl resolves correctly.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="5b-create-an-azure-key-vault-and-a-self-signed-wildcard-cert">5b. Create an Azure Key Vault and a self-signed wildcard cert<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#5b-create-an-azure-key-vault-and-a-self-signed-wildcard-cert" class="hash-link" aria-label="Direct link to 5b. Create an Azure Key Vault and a self-signed wildcard cert" title="Direct link to 5b. Create an Azure Key Vault and a self-signed wildcard cert" translate="no">​</a></h4>
<p>We'll mint a self-signed wildcard cert directly in Key Vault so it covers both <code>a.ga-demo.dev</code> and <code>b.ga-demo.dev</code> from a single certificate. (For production, import a CA-issued PFX with <code>az keyvault certificate import</code> instead.)</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az keyvault create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name $KV_NAME \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $RESOURCE_GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --location $LOCATION \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-rbac-authorization true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Cert policy: wildcard subject + SAN so one cert covers every sub-host of the zone</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cat &gt; /tmp/cert-policy.json &lt;&lt;EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "issuerParameters": { "name": "Self" },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "keyProperties": { "exportable": true, "keyType": "RSA", "keySize": 2048, "reuseKey": false },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "secretProperties": { "contentType": "application/x-pkcs12" },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "x509CertificateProperties": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "subject": "CN=*.${ZONE_NAME}",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "subjectAlternativeNames": { "dnsNames": ["*.${ZONE_NAME}", "${ZONE_NAME}"] },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "validityInMonths": 12,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "keyUsage": ["digitalSignature", "keyEncipherment"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az keyvault certificate create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --vault-name $KV_NAME \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name approuting-demo-cert \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --policy @/tmp/cert-policy.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Capture the unversioned cert URI so the listener tracks rotations automatically</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export CERT_URI=$(az keyvault certificate show \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --vault-name $KV_NAME \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name approuting-demo-cert \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --query id -o tsv | sed 's|/[^/]*$||')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo "Cert URI: $CERT_URI"</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="5c-create-a-managed-identity-role-assignments-and-federated-credentials">5c. Create a managed identity, role assignments, and federated credentials<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#5c-create-a-managed-identity-role-assignments-and-federated-credentials" class="hash-link" aria-label="Direct link to 5c. Create a managed identity, role assignments, and federated credentials" title="Direct link to 5c. Create a managed identity, role assignments, and federated credentials" translate="no">​</a></h4>
<p>Both the operator's DNS controller and the gateway listener's TLS sync authenticate to Azure as a user-assigned managed identity (UAMI), federated to a Kubernetes ServiceAccount via the cluster's OIDC issuer.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 1. Create the UAMI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az identity create --resource-group $RESOURCE_GROUP --name $UAMI_NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export UAMI_CLIENT_ID=$(az identity show --resource-group $RESOURCE_GROUP --name $UAMI_NAME --query clientId -o tsv)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export UAMI_PRINCIPAL_ID=$(az identity show --resource-group $RESOURCE_GROUP --name $UAMI_NAME --query principalId -o tsv)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 2. Grant DNS Zone Contributor on the zone, Key Vault Secrets User on the vault</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az role assignment create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --assignee-object-id $UAMI_PRINCIPAL_ID \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --assignee-principal-type ServicePrincipal \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --role "DNS Zone Contributor" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --scope $ZONE_ID</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az role assignment create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --assignee-object-id $UAMI_PRINCIPAL_ID \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --assignee-principal-type ServicePrincipal \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --role "Key Vault Secrets User" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --scope $(az keyvault show --name $KV_NAME --query id -o tsv)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export OIDC_ISSUER=$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER --query oidcIssuerProfile.issuerUrl -o tsv)</span><br></span></code></pre></div></div>
<p>We'll create the <code>app-a</code> and <code>app-b</code> namespaces now and federate the same UAMI to a <code>$SA_NAME</code> ServiceAccount in each. Federated credentials are subject-specific — each <code>(namespace, ServiceAccount)</code> pair needs its own:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">for ns in app-a app-b; do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kubectl create namespace $ns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  az identity federated-credential create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --identity-name $UAMI_NAME \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --resource-group $RESOURCE_GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --name approuting-demo-fic-$ns \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --issuer $OIDC_ISSUER \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --subject "system:serviceaccount:$ns:$SA_NAME" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --audiences "api://AzureADTokenExchange"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kubectl apply -n $ns -f - &lt;&lt;EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: ServiceAccount</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: $SA_NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  annotations:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azure.workload.identity/client-id: $UAMI_CLIENT_ID</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  labels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azure.workload.identity/use: "true"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">done</span><br></span></code></pre></div></div>
<p>The <code>azure.workload.identity/use: "true"</code> label is required; it's what tells the AKS Workload Identity webhook to inject the projected OIDC token volume and the <code>AZURE_*</code> env vars into pods that consume this SA. Without it, the operator's placeholder pod gets a default Kubernetes SA token instead of one signed by your cluster's OIDC issuer, and Entra rejects it with <code>AADSTS700211: No matching federated identity record</code>.</p>
<p>The same ServiceAccount drives both the DNS controller and the listener's TLS sync. One identity, two roles in Azure, one SA per namespace.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="5d-deploy-two-gateways-in-two-namespaces">5d. Deploy two gateways in two namespaces<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#5d-deploy-two-gateways-in-two-namespaces" class="hash-link" aria-label="Direct link to 5d. Deploy two gateways in two namespaces" title="Direct link to 5d. Deploy two gateways in two namespaces" translate="no">​</a></h4>
<p>Deploy <code>httpbin</code> in each namespace, then create one <code>Gateway</code> per namespace with an HTTPS listener pointing at the Key Vault cert URI and the SA. Each gateway covers its own sub-host:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">for ns in app-a app-b; do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kubectl apply -n $ns -f https://raw.githubusercontent.com/istio/istio/release-1.27/samples/httpbin/httpbin.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">done</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">for pair in "app-a:a" "app-b:b"; do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ns=${pair%%:*}; sub=${pair##*:}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  fqdn=${sub}.${ZONE_NAME}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kubectl apply -n $ns -f - &lt;&lt;EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: ${sub}-gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  labels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    app: approuting-demo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    zone: ${sub}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  gatewayClassName: approuting-istio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  listeners:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - name: https</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    hostname: $fqdn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    port: 443</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    protocol: HTTPS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tls:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      mode: Terminate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      options:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        kubernetes.azure.com/tls-cert-keyvault-uri: $CERT_URI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        kubernetes.azure.com/tls-cert-service-account: $SA_NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    allowedRoutes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      namespaces:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        from: Same</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: HTTPRoute</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: ${sub}-route</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  parentRefs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - name: ${sub}-gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  hostnames: ["$fqdn"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  rules:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - matches:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - path:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        type: PathPrefix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        value: /get</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    backendRefs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - name: httpbin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      port: 8000</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">done</span><br></span></code></pre></div></div>
<p>The TLS options tell the App Routing operator to:</p>
<ol>
<li>Resolve <code>$CERT_URI</code> against the Key Vault, using the workload identity bound to <code>$SA_NAME</code> in the listener's namespace.</li>
<li>Provision a <code>SecretProviderClass</code> named <code>kv-gw-cert-&lt;gateway&gt;-&lt;listener&gt;</code>.</li>
<li>Sync the certificate as a <code>kubernetes.io/tls</code> Secret of the same name in the listener's namespace.</li>
<li>Patch the listener's <code>certificateRefs</code> to point at the Secret.</li>
</ol>
<p>You can confirm the SPCs and Secrets exist:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get secretproviderclass,secret -n app-a</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get secretproviderclass,secret -n app-b</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="5e-scenario-a--clusterexternaldns-records-for-both-gateways">5e. Scenario A — <code>ClusterExternalDNS</code>: records for both gateways<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#5e-scenario-a--clusterexternaldns-records-for-both-gateways" class="hash-link" aria-label="Direct link to 5e-scenario-a--clusterexternaldns-records-for-both-gateways" title="Direct link to 5e-scenario-a--clusterexternaldns-records-for-both-gateways" translate="no">​</a></h4>
<p><code>ClusterExternalDNS</code> is cluster-scoped: a single CR points at one or more Azure DNS zones and watches Gateway resources cluster-wide. The operator deploys an <code>external-dns</code> instance in the namespace named under <code>resourceNamespace</code>; the SA in that namespace authenticates to Azure DNS.</p>
<p>Apply it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f - &lt;&lt;EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: approuting.kubernetes.azure.com/v1alpha1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: ClusterExternalDNS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: demo-cluster-dns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resourceName: demo-cluster-dns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resourceNamespace: app-a</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  dnsZoneResourceIDs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - $ZONE_ID</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resourceTypes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  identity:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    type: workloadIdentity</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    serviceAccount: $SA_NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<p>After a minute, both records show up in the zone:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az network dns record-set a list --resource-group $RESOURCE_GROUP --zone-name $ZONE_NAME -o table</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Name    ResourceGroup              Ttl    Type    AutoRegistered    Metadata</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">------  -------------------------  -----  ------  ----------------  --------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">a       &lt;your-rg&gt;                  300    A       False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">b       &lt;your-rg&gt;                  300    A       False</span><br></span></code></pre></div></div>
<p>That's <code>ClusterExternalDNS</code>: one CR, one <code>external-dns</code> deployment, records for both gateways across both namespaces.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="5f-scenario-b--externaldns-namespace-scoped-with-label-filtering">5f. Scenario B — <code>ExternalDNS</code>: namespace-scoped, with label filtering<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#5f-scenario-b--externaldns-namespace-scoped-with-label-filtering" class="hash-link" aria-label="Direct link to 5f-scenario-b--externaldns-namespace-scoped-with-label-filtering" title="Direct link to 5f-scenario-b--externaldns-namespace-scoped-with-label-filtering" translate="no">​</a></h4>
<p>Now flip the model. Delete the <code>ClusterExternalDNS</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl delete clusterexternaldns demo-cluster-dns</span><br></span></code></pre></div></div>
<p>To make the namespace + label scope concrete, deploy a <strong>third</strong> gateway in <code>app-a</code> labeled <code>zone=c</code>. We'll point a namespace-scoped <code>ExternalDNS</code> at it and watch a fresh record appear — without touching the existing <code>a</code> and <code>b</code> records:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -n app-a -f - &lt;&lt;EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: c-gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  labels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    app: approuting-demo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    zone: c</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  gatewayClassName: approuting-istio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  listeners:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - name: https</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    hostname: c.${ZONE_NAME}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    port: 443</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    protocol: HTTPS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tls:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      mode: Terminate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      options:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        kubernetes.azure.com/tls-cert-keyvault-uri: $CERT_URI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        kubernetes.azure.com/tls-cert-service-account: $SA_NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    allowedRoutes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      namespaces:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        from: Same</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: HTTPRoute</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: c-route</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  parentRefs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - name: c-gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  hostnames: ["c.${ZONE_NAME}"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  rules:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - matches:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - path:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        type: PathPrefix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        value: /get</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    backendRefs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - name: httpbin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      port: 8000</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl wait -n app-a --for=condition=programmed gateway c-gateway --timeout=240s</span><br></span></code></pre></div></div>
<p>Now create a namespace-scoped <code>ExternalDNS</code> in <code>app-a</code> with a label filter for <code>zone=c</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -n app-a -f - &lt;&lt;EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: approuting.kubernetes.azure.com/v1alpha1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: ExternalDNS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: demo-ns-dns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resourceName: demo-ns-dns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  dnsZoneResourceIDs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - $ZONE_ID</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resourceTypes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  identity:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    type: workloadIdentity</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    serviceAccount: $SA_NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  filters:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    gatewayLabels: "zone=c"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<p>Two scoping rules apply at once:</p>
<ol>
<li><strong>Namespace scope</strong> — <code>ExternalDNS</code> only watches resources in its own namespace, so <code>b-gateway</code> in <code>app-b</code> is invisible to it.</li>
<li><strong>Label filter</strong> — within <code>app-a</code>, only gateways carrying <code>zone=c</code> are picked up. The label key/value is rendered into the underlying <code>external-dns</code> controller's label selector.</li>
</ol>
<p>After about a minute, a new <code>c</code> record appears alongside whatever was already there from the cluster-scoped run:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az network dns record-set a list --resource-group $RESOURCE_GROUP --zone-name $ZONE_NAME -o table</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Name    ResourceGroup              Ttl    Type    AutoRegistered    Metadata</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">------  -------------------------  -----  ------  ----------------  --------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">a       &lt;your-rg&gt;                  300    A       False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">b       &lt;your-rg&gt;                  300    A       False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c       &lt;your-rg&gt;                  300    A       False</span><br></span></code></pre></div></div>
<p>The new namespace-scoped controller is responsible for <code>c</code> only. <code>a-gateway</code> (also in <code>app-a</code>) is excluded by the label filter; <code>b-gateway</code> is excluded by the namespace filter.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="5g-verify-https-end-to-end">5g. Verify HTTPS end-to-end<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#5g-verify-https-end-to-end" class="hash-link" aria-label="Direct link to 5g. Verify HTTPS end-to-end" title="Direct link to 5g. Verify HTTPS end-to-end" translate="no">​</a></h4>
<p>Grab the zone's authoritative nameserver, resolve the FQDN through it, and feed the result straight into <code>curl --resolve</code>. That keeps the test self-contained — you don't need the registrar pointing at Azure DNS or your local resolver to know about the zone:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NS=$(az network dns zone show --resource-group $RESOURCE_GROUP --name $ZONE_NAME --query 'nameServers[0]' -o tsv | sed 's/\.$//')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">GATEWAY_IP=$(dig +short @${NS} a.${ZONE_NAME} | tail -1)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo "Resolved a.${ZONE_NAME} -&gt; $GATEWAY_IP via $NS"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Self-signed cert, so -k. Use --cacert if you exported the chain.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">curl -k -I --resolve "a.${ZONE_NAME}:443:${GATEWAY_IP}" "https://a.${ZONE_NAME}/get"</span><br></span></code></pre></div></div>
<p>You should see <code>HTTP/2 200</code>. The certificate Envoy presents is the one synced from Key Vault. You can re-tail the gateway deployment's logs (<code>kubectl logs deploy/a-gateway-approuting-istio -n app-a</code>) to see the access log line for the request.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="next-steps">Next steps<a href="https://blog.aks.azure.com/2026/06/10/app-routing-gateway-api-ga#next-steps" class="hash-link" aria-label="Direct link to Next steps" title="Direct link to Next steps" translate="no">​</a></h2>
<ul>
<li><strong>Migrate</strong>: If you're on the NGINX add-on, see the <a href="https://learn.microsoft.com/azure/aks/app-routing-nginx-to-gateway-api-migration" target="_blank" rel="noopener noreferrer">Ingress to Gateway API migration guide</a>. Managed NGINX support continues through November 2026.</li>
<li><strong>Deepen</strong>: The full reference docs are at <a href="https://learn.microsoft.com/azure/aks/app-routing-gateway-api" target="_blank" rel="noopener noreferrer">Configure ingress with the Kubernetes Gateway API</a> and <a href="https://learn.microsoft.com/azure/aks/app-routing-gateway-api-tls" target="_blank" rel="noopener noreferrer">Secure ingress with the application routing Gateway API implementation</a>.</li>
<li><strong>Track</strong>: <a href="https://releases.aks.azure.com/AKSRelease" target="_blank" rel="noopener noreferrer">AKS release tracker</a> and the <a href="https://github.com/Azure/AKS/releases" target="_blank" rel="noopener noreferrer">AKS release notes</a>.</li>
<li><strong>Feedback</strong>: Open an issue at <a href="https://github.com/Azure/AKS/issues" target="_blank" rel="noopener noreferrer">Azure/AKS issues</a>.</li>
</ul>]]></content:encoded>
            <category>App Routing</category>
            <category>Gateway API</category>
            <category>Networking</category>
            <category>Istio</category>
            <category>DNS</category>
        </item>
        <item>
            <title><![CDATA[Announcing the public preview of AKS on bare metal]]></title>
            <link>https://blog.aks.azure.com/2026/06/02/aks-baremetal-public-preview</link>
            <guid>https://blog.aks.azure.com/2026/06/02/aks-baremetal-public-preview</guid>
            <pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[AKS on bare metal is now available in public preview!]]></description>
            <content:encoded><![CDATA[<p><em>AKS runs everywhere — now on bare metal at the edge</em></p>
<p>Our vision for Azure Kubernetes Service has always been simple: AKS runs everywhere. The same Azure Kubernetes Service you run across Azure regions is the same platform you run in sovereign and private clouds, and on OEM hardware. One platform, every substrate — so that the skills, the tooling, and the operational model you already know come with you wherever your applications need to run.</p>
<p>Today, we're adding the newest member to that family — and it answers one of the strongest signals we've heard from customers at the edge. <strong>AKS now runs directly on bare-metal, small-form-factor devices at the edge, available today in public preview.</strong></p>
<p><img decoding="async" loading="lazy" alt="An overview of the AKS on bare metal resource in Azure Portal" src="https://blog.aks.azure.com/assets/images/AKS-on-baremetal-Overview-2a78eabb61792ef54d7e1bd5bcd7a1b1.png" width="2839" height="1066" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-bare-metal">Why bare metal<a href="https://blog.aks.azure.com/2026/06/02/aks-baremetal-public-preview#why-bare-metal" class="hash-link" aria-label="Direct link to Why bare metal" title="Direct link to Why bare metal" translate="no">​</a></h2>
<p>For years, customers have told us the same thing: the edge forces hard trade-offs on Kubernetes. They want the consistency of a managed Kubernetes platform in the places where their business actually happens — the retail back room, the factory floor, the quick-service restaurant, the branch office, the remote site. But the edge is not the datacenter. Space, power, and budget are constrained. Hardware is small and sometimes ruggedized. Connectivity is intermittent, and sites often need to keep running through disconnection. Given the Kubernetes control plane runs locally on the device, deployed workloads continue to operate normally during connectivity loss. Only portal visibility and Azure management actions are interrupted until the connection is restored.</p>
<p>What customers have asked us for is precise: run real Kubernetes directly on the hardware, with no hypervisor underneath and no virtualization layer to license or maintain; do it on lightweight edge hardware rather than datacenter-class servers; and let it keep operating through intermittent connectivity.</p>
<p>That is exactly what AKS on bare metal delivers. This is Azure Kubernetes Service running directly on the device with no hypervisor, on small-form-factor hardware, and resilient to intermittent connectivity. It is not a different product for the edge. It is the same AKS, supporting a new class of hardware at the edge.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="from-power-on-to-a-running-aks-cluster">From power-on to a running AKS cluster<a href="https://blog.aks.azure.com/2026/06/02/aks-baremetal-public-preview#from-power-on-to-a-running-aks-cluster" class="hash-link" aria-label="Direct link to From power-on to a running AKS cluster" title="Direct link to From power-on to a running AKS cluster" translate="no">​</a></h2>
<p>The experience is deliberately simple. On-site, someone plugs in a USB drive with an OS image from the Azure portal, powers on the device, waits a couple of minutes, and removes the drive. That's the only step that happens at the location. During this <a href="https://techcommunity.microsoft.com/blog/azurearcblog/announcing-public-preview-simplified-machine-provisioning-for-azure-local/4496811" target="_blank" rel="noopener noreferrer">zero-touch provisioning</a> process, a device voucher is written back to the USB drive and all configuration shifts to Azure — no deep infrastructure expertise required on site.</p>
<p>From there, everything happens in Azure. The machine is onboarded through the <a href="https://learn.microsoft.com/en-us/azure/azure-local/whats-new" target="_blank" rel="noopener noreferrer">Azure Local small form factor</a> creation experience — create a site, upload the voucher, and the machine comes up as Provisioned. The cluster creation looks exactly like AKS anywhere else: configure Azure RBAC, set the control-plane networking, enable Azure Monitor, and deploy. When it completes, you have an AKS cluster running on Azure Linux, on bare metal, fully manageable from Azure: workloads, services, ingress, and Kubernetes upgrades, all without leaving the portal.</p>
<p><img decoding="async" loading="lazy" alt="Upgrade your AKS on bare metal cluster via the Azure Portal" src="https://blog.aks.azure.com/assets/images/aks-on-baremetal-upgrade-66b3fc6408b6af0aea7e0c0668c5c593.png" width="1630" height="1059" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="one-consistent-aks-experience-from-cloud-to-edge">One consistent AKS experience, from cloud to edge<a href="https://blog.aks.azure.com/2026/06/02/aks-baremetal-public-preview#one-consistent-aks-experience-from-cloud-to-edge" class="hash-link" aria-label="Direct link to One consistent AKS experience, from cloud to edge" title="Direct link to One consistent AKS experience, from cloud to edge" translate="no">​</a></h2>
<p>Provisioning is only half the story. What matters at the edge is how you operate the cluster over time — and here the principle is simple: AKS on bare metal is just an AKS cluster. There's no edge-specific console, no separate operational model, no exceptions to learn.</p>
<p>Because it's a first-class AKS cluster, it shows up in Azure Kubernetes Fleet Manager right alongside your cloud clusters, managed and monitored with the same controls and the same tooling you already use everywhere else. The same AKS, the same experience, consistent across cloud and edge — now including bare metal.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="available-today">Available today<a href="https://blog.aks.azure.com/2026/06/02/aks-baremetal-public-preview#available-today" class="hash-link" aria-label="Direct link to Available today" title="Direct link to Available today" translate="no">​</a></h2>
<p>This is what "AKS everywhere" was always meant to become: not a fragmented collection of edge-specific products and tools, but one Kubernetes platform that follows your applications to every place they need to run — now including the smallest, most constrained, and most remote hardware at the edge.</p>
<p><strong>AKS on bare metal is now in public preview.</strong> We can't wait to see what you build on it.</p>
<p>Join the preview today at <a href="https://aka.ms/aks-edge-baremetal" target="_blank" rel="noopener noreferrer">aka.ms/aks-edge-baremetal</a>.</p>]]></content:encoded>
            <category>AKS on bare metal</category>
            <category>Azure Kubernetes Fleet Manager</category>
        </item>
        <item>
            <title><![CDATA[Scaling multi-node LLM inference with NVIDIA Dynamo-Grove on AKS (Part 4)]]></title>
            <link>https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4</link>
            <guid>https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4</guid>
            <pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how NVIDIA Grove scheduler works with Dynamo to schedule, start, and scale inference deployments to match the underlying inference architecture.]]></description>
            <content:encoded><![CDATA[<p><em>This blog post is co-authored with <a href="https://www.linkedin.com/in/nikharmaheshwari/" target="_blank" rel="noopener noreferrer">Nikhar Maheshwari</a>,
<a href="https://www.linkedin.com/in/anish-maddipoti/" target="_blank" rel="noopener noreferrer">Anish Maddipoti</a>,
<a href="https://www.linkedin.com/in/rohan-s-varma/" target="_blank" rel="noopener noreferrer">Rohan Varma</a>,
<a href="https://www.linkedin.com/in/clement-ai/" target="_blank" rel="noopener noreferrer">Clement Pakkam Isaac</a>, and
<a href="https://www.linkedin.com/in/stephen-mcc/" target="_blank" rel="noopener noreferrer">Stephen Mccoulough</a> from NVIDIA.</em></p>
<p>In the <a href="https://blog.aks.azure.com/tags/dynamo-series" target="_blank" rel="noopener noreferrer">first three blog posts</a> of this series, we introduced <a href="https://www.nvidia.com/en-us/ai/dynamo/" target="_blank" rel="noopener noreferrer">NVIDIA Dynamo</a> on AKS, covered SLO-driven scaling with the Dynamo Planner and Profiler, and explored KV-cache-aware routing to establish the foundations for high-performance LLM serving. In this post, we move up one layer of the inference stack: how to describe and operate the distributed inference workload on a Kubernetes cluster.</p>
<p>As inference deployments evolve from simple model-serving replicas to systems with disaggregated prefill and decode phases, multi-node model instances, and explicit startup dependencies, an inference deployment benefits from a single API that describes the full serving pipeline: which roles exist, how they relate, and what constitutes a <strong>deployable unit</strong> of inference-serving capacity.</p>
<p><a href="https://developer.nvidia.com/grove" target="_blank" rel="noopener noreferrer">NVIDIA Grove</a> addresses this layer of the stack with a Kubernetes-native API for describing an AI inference service — so the platform can schedule, start, and scale the deployment in units that match the inference architecture.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-kubernetes-orchestration-gap-for-distributed-inference">The Kubernetes orchestration gap for distributed inference<a href="https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4#the-kubernetes-orchestration-gap-for-distributed-inference" class="hash-link" aria-label="Direct link to The Kubernetes orchestration gap for distributed inference" title="Direct link to The Kubernetes orchestration gap for distributed inference" translate="no">​</a></h2>
<p>Modern AI inference workloads are fundamentally different from the traditional distributed workloads built with independent, stateless microservices. A production LLM deployment may include distinct roles – frontend services, KV-aware routers, prefill and decode workers – that are tightly coupled in how they must be scheduled, started, and scaled. They require scheduling in groups, starting up in a specific order, and scaling in ratios that preserve an end-to-end serving path. For larger models, a single prefill or decode instance may span several pods; where a complete deployment of each instance is necessary for a functional serving capacity.</p>
<p>Kubernetes primitives can independently schedule and run these components, but they do not natively understand the inter-component relationships required for the deployment to operate as an inference service:</p>
<ul>
<li>Which pods form one usable model instance?</li>
<li>Which roles must start before others?</li>
<li>Which components scale independently, and which must scale as a group?</li>
</ul>
<p>Using existing Kubernetes primitives alone, operations teams are forced to spread these considerations across bespoke manifests, labels, scripts, or custom controllers. This can create orchestration challenges and worsen the deployment observability.</p>
<p><a href="https://lws.sigs.k8s.io/docs/overview/" target="_blank" rel="noopener noreferrer">LeaderWorkerSet (LWS)</a> is an Upstream API that addresses a part of this problem by managing a leader pod and its worker pods as a replicated unit, which generally maps well to multi-node model instances. A full Dynamo-style deployment, however, can contain multiple leader-worker units across prefill and decode, plus routing and other service components. LWS may model those leader-worker groups, but it is not intended to describe the full inference service across them. Additionally, LWS couples multi-node scaling units with a leader-worker pattern, and not all multi-node use cases map to this cleanly. Overall, scheduling, startup, and scaling for such modern deployments require a more flexible Kubernetes-native workload abstraction.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="nvidia-grove-for-kubernetes-native-ai-scheduling">NVIDIA Grove for Kubernetes-native AI scheduling<a href="https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4#nvidia-grove-for-kubernetes-native-ai-scheduling" class="hash-link" aria-label="Direct link to NVIDIA Grove for Kubernetes-native AI scheduling" title="Direct link to NVIDIA Grove for Kubernetes-native AI scheduling" translate="no">​</a></h2>
<p>NVIDIA Grove is an open-source Kubernetes API, available as a modular component of NVIDIA Dynamo, for defining both simple single-node and complex multi-node AI workloads. Where standard Kubernetes resources describe individual pods and services, Grove enables you to describe an entire inference serving system’s components and orchestration requirements as one object: routing, prefill, decode, leader-worker groups/multi-node units, startup dependencies, and scaling boundaries — all expressed in a single workload specification.</p>
<p>From that specification, Grove takes responsibility for coordinating the Kubernetes resources required to run the service. Scheduling, startup, and scaling have a shared view of the workload: which pods form a usable instance and which roles belong together. Scaling actions can target a single role, a complete multi-node model instance, or a full service replica. Startup dependencies between roles are part of the workload definition rather than external scripts.</p>
<p>The result is a workload API that matches how distributed inference systems are actually built: individual roles with different resource profiles, grouped model instances that must scale together, and complete service replicas that represent usable end-to-end serving capacity.</p>
<p>In an inference deployment, Dynamo provides the serving stack: inference workers, routing, the Planner, KV cache management, and backend integrations such as vLLM, TensorRT-LLM, and SGLang. Grove defines the Kubernetes orchestration layer around this stack, a.k.a. the contract between the inference system and the cluster. Grove can also be deployed standalone or integrated with other high-performance inference frameworks.</p>
<p><img decoding="async" loading="lazy" alt="Figure 1: End-to-end Grove workflow" src="https://blog.aks.azure.com/assets/images/grove_workflow-e6a7c703dc2effb7808f2fc41f43f6ee.png" width="4133" height="2088" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-grove-models-the-workload">How Grove models the workload<a href="https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4#how-grove-models-the-workload" class="hash-link" aria-label="Direct link to How Grove models the workload" title="Direct link to How Grove models the workload" translate="no">​</a></h2>
<p>Grove's user-facing model is built around three hierarchical primitives that together describe the full shape of an inference deployment, from individual roles up to the complete service:</p>
<table><thead><tr><th>Grove primitive</th><th>What it represents</th><th>Example in model inference</th></tr></thead><tbody><tr><td>PodClique</td><td>A group of pods with the same role and pod template. It has its own replica count, resource needs, and optional autoscaling policy</td><td>Router pods, prefill workers, decode workers, frontend pods</td></tr><tr><td>PodCliqueScalingGroup</td><td>A group of PodClique objects that must scale together while preserving role ratios</td><td>A multi-node prefill instance with one leader and four workers</td></tr><tr><td>PodCliqueSet</td><td>The top-level workload object that describes the full inference system</td><td>Router + prefill group + decode group, with startup order and scaling policy</td></tr></tbody></table>
<p>Using this hierarchy, Grove generates the lower-level Kubernetes resources the cluster needs to schedule, start, and scale the workload. Users work at the inference deployment level, while Grove translates the intent into Grove managed pods and other resources, including the scheduler-facing PodGang resources that encode the scheduling constraints.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="coordinating-scheduling-startup-and-scalability">Coordinating scheduling, startup, and scalability<a href="https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4#coordinating-scheduling-startup-and-scalability" class="hash-link" aria-label="Direct link to Coordinating scheduling, startup, and scalability" title="Direct link to Coordinating scheduling, startup, and scalability" translate="no">​</a></h2>
<p>Once Grove has the workload specification, it uses the same hierarchy to guide how the workload is scheduled, started, and scaled.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="scheduling">Scheduling<a href="https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4#scheduling" class="hash-link" aria-label="Direct link to Scheduling" title="Direct link to Scheduling" translate="no">​</a></h3>
<p>For multi-node inference, serving capacity arrives as a collection of complete instances. A prefill or decode instance that spans several pods can serve traffic only when the full set can run together. Gang scheduling gives Kubernetes an all-or-nothing contract: either all pods in the instance are scheduled together, or none are. Grove creates the scheduler-facing PodGang resources automatically from the workload specification, so this guarantee is part of the workload model rather than a rule that teams have to enforce separately.</p>
<p>Gang scheduling ensures that each individual component of a deployment is allocated as a complete unit, for example, a full prefill instance or a full decode instance. However, this alone does not guarantee that the deployment can actually serve end-to-end requests: the scheduler might schedule several complete prefill instances without any matching decode capacity, or vice versa.</p>
<p>Grove addresses this limitation with hierarchical gang scheduling. Instead of enforcing completeness only at the instance level, Grove also enforces it at the service level. The workload specifies higher-level viability constraints, for example, that at least one complete prefill instance and one complete decode instance must be scheduled together before the deployment is considered ready. The scheduler therefore treats these interdependent components as a “higher-level gang”: it schedules the required combination only when enough resources are available. This prevents the system from allocating individually complete but functionally unusable configurations that cannot process requests end to end.</p>
<p>On an AKS cluster, Grove-managed inference pods can be scheduled by a gang-aware scheduler like the <a href="https://github.com/kai-scheduler/KAI-Scheduler" target="_blank" rel="noopener noreferrer">KAI Scheduler</a>, an open-source Kubernetes-native scheduler built for AI workloads with native PodGang awareness. Grove defines the inference workload - the roles, groups, dependencies, and scaling boundaries - while KAI gives Kubernetes the scheduling behavior needed to place those pods as complete units. Together, they enable these capabilities to be expressed and enforced cleanly within the Kubernetes-native infrastructure.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>When deploying Dynamo workloads with Grove and KAI scheduler, it is recommended that the cluster is set up with dedicated resources for the KAI scheduler to avoid scheduling conflicts. General-purpose workloads can continue to use the default kube-scheduler on separate node pools, so the two schedulers do not compete for the same node capacity.</p></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="startup">Startup<a href="https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4#startup" class="hash-link" aria-label="Direct link to Startup" title="Direct link to Startup" translate="no">​</a></h3>
<p>Scheduling is only the first step. Distributed serving systems often have startup dependencies; for example, workers need to be discoverable before leaders start, and routers or frontends may need to be ready before model workers register. Grove integrates <strong>startup order</strong> with the workload definition through <code>cliqueStartupType</code> and <code>startsAfter</code>, instead of leaving it to scripts or external conventions.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="scaling">Scaling<a href="https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4#scaling" class="hash-link" aria-label="Direct link to Scaling" title="Direct link to Scaling" translate="no">​</a></h3>
<p>Grove has multiple scaling boundaries in a hierarchy model: a PodClique for one role, a PodCliqueScalingGroup for a complete leader-worker unit, and a PodCliqueSet for the full service. This preserves independent scaling even when scheduling is coordinated: prefill and decode can be scheduled as viable units while still scaling separately in response to various bottlenecks.</p>
<p>Different scaling boundaries also integrate well with the Dynamo Planner (covered in <a href="https://blog.aks.azure.com/2026/01/22/dynamo-on-aks-part-2" target="_blank" rel="noopener noreferrer">Part 2 of this blog series</a>). While the Planner reasons about traffic shape, sequence lengths, and time-to-first-token (TTFT) or inter-token latency (ITL) targets to decide when capacity should change, Grove ensures this decision targets the appropriate Grove workload boundary: a PodClique for a single role, a PodCliqueScalingGroup for a complete leader-worker instance, or the PodCliqueSet for the end-to-end inference service. Ultimately, Planner-driven scale-out doesn't just add pods; it adds a complete, gang-scheduled inference instance that is ready to serve.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="modeling-inference-deployments-with-grove">Modeling inference deployments with Grove<a href="https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4#modeling-inference-deployments-with-grove" class="hash-link" aria-label="Direct link to Modeling inference deployments with Grove" title="Direct link to Modeling inference deployments with Grove" translate="no">​</a></h2>
<p>Grove's <a href="https://github.com/ai-dynamo/grove/blob/main/docs/api-reference/operator-api.md#podcliqueset" target="_blank" rel="noopener noreferrer">PodCliqueSet API</a> is flexible enough to cover a range of inference deployment shapes:</p>
<p><strong>Aggregated serving</strong>: where a single PodClique is sufficient; each worker handles the full request lifecycle, and scaling means adding more copies of that worker pod.</p>
<p><strong>Disaggregated serving</strong>: router, prefill, and decode become separate PodCliques, each scaling independently in response to different traffic pressures.</p>
<p><strong>Multi-node</strong>: when a single model instance spans multiple pods, a PodCliqueScalingGroup binds the leader and worker PodCliques into one scaling unit.</p>
<p>The Grove GitHub repository includes <a href="https://github.com/ai-dynamo/grove/tree/main/operator/samples/user-guide/01_core-concepts" target="_blank" rel="noopener noreferrer">reference manifests</a> for these deployment patterns, giving teams concrete starting points for mapping their own serving architecture into PodCliques, PodCliqueScalingGroups, and PodCliqueSets.</p>
<p>See Grove in action below as it orchestrates a multi-node Llama 3.1 70B deployment on AKS. The demo serves <a href="https://huggingface.co/nvidia/Llama-3.1-70B-Instruct-FP8" target="_blank" rel="noopener noreferrer">Llama-3.1-70B-Instruct-FP8</a> on 16x NVIDIA H100 (2 nodes of <a href="https://learn.microsoft.com/azure/virtual-machines/sizes/gpu-accelerated/ndh100v5-series" target="_blank" rel="noopener noreferrer">Azure Standard_ND96isr_H100_v5</a>) with tp=16. The walkthrough shows a DynamoGraphDeployment being translated into Grove resources, worker pods landing across H100 nodes, and scale-out adding a second gang-scheduled two-pod worker as a grouped unit.</p>
<p><a href="https://asciinema.org/a/TLoDssztIJ3XZVpo" target="_blank" rel="noopener noreferrer"><img decoding="async" loading="lazy" src="https://asciinema.org/a/TLoDssztIJ3XZVpo.svg" alt="Demo: Multi-node inference with NVIDIA Dynamo + Grove on AKS" class="img_ev3q"></a></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="looking-ahead">Looking ahead<a href="https://blog.aks.azure.com/2026/06/02/dynamo-on-aks-part-4#looking-ahead" class="hash-link" aria-label="Direct link to Looking ahead" title="Direct link to Looking ahead" translate="no">​</a></h2>
<p>By introducing this workload layer, Grove provides Kubernetes with a structured contract for orchestrating the gang scheduling and scaling requirements of AI workloads. This also provides the foundation for handling complex challenges that modern inference workloads are facing today.</p>
<p>One such challenge is <strong>topology-aware scheduling</strong>. As deployments span multiple nodes and accelerator domains, scheduling decisions become critical to performance. Without topology awareness, gang-scheduled instances may be placed on nodes that are physically or network-topologically distant from one another, introducing <em>communication bottlenecks</em> that undermine the efficiency that the inference stack is designed to deliver.</p>
<p>Rolling updates introduce another layer of complexity. In disaggregated inference, prefill and decode components usually have compatibility constraints across framework versions, requiring cross-version traffic to be blocked during upgrades. This can strand capacity across versions: for example, a rollout might leave eight old-version prefill instances but only two old-version decode instances, reducing the amount of end-to-end capacity that can actually serve requests. Updating these systems safely requires rollout behavior that <em>preserves viable serving capacity</em> across the pipeline, rather than updating each role as an independent pool of pods.</p>
<p>In Part 5, we’ll build on the Grove workload model introduced here and explore how this structured view of the serving pipeline enables more advanced orchestration, including topology-aware scheduling for distributed inference and safer rolling updates for disaggregated serving pipelines. We’ll also share how we’re collaborating with the NVIDIA Grove and Dynamo communities toward a shared goal: building a Kubernetes-native orchestration layer for the demands of modern inference.</p>]]></content:encoded>
            <category>Dynamo on AKS series</category>
            <category>AI</category>
            <category>Performance</category>
            <category>Open Source</category>
        </item>
        <item>
            <title><![CDATA[Apply Copy Fail and DirtyFrag CVE mitigations at-scale using Azure Kubernetes Fleet Manager]]></title>
            <link>https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager</link>
            <guid>https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager</guid>
            <pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[See how to apply mitigations for CVE-2026-31431 (Copy Fail) and CVE-2026-43284 / CVE-2026-43500 (DirtyFrag) across AKS clusters using Azure Kubernetes Fleet Manager with a DaemonSet and node image upgrades.]]></description>
            <content:encoded><![CDATA[<p>This post shows how to use Azure Kubernetes Fleet Manager to simplify the safe rollout of mitigations for <a href="https://github.com/advisories/GHSA-2274-3hgr-wxv6" target="_blank" rel="noopener noreferrer">CVE-2026-31431</a> ("Copy Fail") and <a href="https://github.com/advisories/GHSA-mmw8-mxmc-8w2r" target="_blank" rel="noopener noreferrer">CVE-2026-43284</a> / CVE-2026-43500 ("DirtyFrag") across multiple AKS clusters. This vulnerability allows a container to escalate to root on the node and impacts AKS Linux nodes until mitigations are applied. Existing nodes require either a node image upgrade or a self-service DaemonSet mitigation.</p>
<p>The approaches covered in this post apply mitigations as follows:</p>
<ol>
<li><a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#immediate-mitigation---daemonset-rollout">Immediate mitigation</a>: deploy a DaemonSet using Fleet Manager resource placement. Best for clusters where a node image upgrade can't yet be applied.</li>
<li><a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#permanent-mitigation---node-image-upgrade">Permanent mitigation</a>: upgrade node images using Fleet Manager update runs. Applies patches when new node image version is available in the cluster's region.</li>
</ol>
<p>The original AKS advisory and detailed mitigation guide for per-cluster application can be found in <a href="https://github.com/Azure/AKS/issues/5753" target="_blank" rel="noopener noreferrer">GitHub Issue 5753</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="before-you-begin">Before you begin<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#before-you-begin" class="hash-link" aria-label="Direct link to Before you begin" title="Direct link to Before you begin" translate="no">​</a></h2>
<ul>
<li>Azure CLI version 2.86.0 or later installed (<a href="https://learn.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest" target="_blank" rel="noopener noreferrer">instructions</a>).</li>
<li>Fleet Manager Azure CLI extension installed and updated.</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az extension add --name fleet</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az extension update --name fleet</span><br></span></code></pre></div></div>
<ul>
<li>A Fleet Manager with a hub cluster (<a href="https://learn.microsoft.com/azure/kubernetes-fleet/quickstart-create-fleet-and-members?pivots=public-hub" target="_blank" rel="noopener noreferrer">instructions</a>). You can add a hub cluster if you are already using Fleet without one.</li>
<li>AKS clusters added as member clusters with update group and member labels set. Groups and labels are required for controlling the rollout via placement and update runs.</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az fleet member create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --fleet-name $FLEET \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name $MEMBER_CLUSTER_NAME \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --update-group canary \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --labels upgroup=canary</span><br></span></code></pre></div></div>
<ul>
<li>kubectl configured for the hub cluster (<a href="https://learn.microsoft.com/azure/kubernetes-fleet/access-fleet-hub-cluster-kubernetes-api?pivots=public-hub" target="_blank" rel="noopener noreferrer">instructions</a>).</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="immediate-mitigation---daemonset-rollout">Immediate mitigation - DaemonSet rollout<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#immediate-mitigation---daemonset-rollout" class="hash-link" aria-label="Direct link to Immediate mitigation - DaemonSet rollout" title="Direct link to Immediate mitigation - DaemonSet rollout" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-important admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>important</div><div class="admonitionContent_BuS1"><p>Make sure to review the steps in <a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#before-you-begin">before you begin</a>, especially adding labels to member clusters.</p></div></div>
<p>For immediate protection, apply a DaemonSet that disables the vulnerable kernel module. The DaemonSet is deployed into its own Namespace on each cluster, making it easy to discern intent and easier to distribute across clusters. Given how DaemonSets function, the protection is applied to the node, even though we don't use the <code>kube-system</code> Namespace as shown in the <a href="https://github.com/Azure/AKS/issues/5753" target="_blank" rel="noopener noreferrer">original AKS mitigation guide</a>.</p>
<p>Fleet Manager's <a href="https://learn.microsoft.com/azure/kubernetes-fleet/quickstart-resource-propagation?tabs=azure-cli" target="_blank" rel="noopener noreferrer">cluster-scoped resource placement</a> enables:</p>
<ul>
<li>Multi-cluster selection for a resource via <a href="https://learn.microsoft.com/azure/kubernetes-fleet/quickstart-resource-propagation?tabs=azure-cli#use-clusterresourceplacement-to-place-resources-onto-member-clusters" target="_blank" rel="noopener noreferrer">ClusterResourcePlacement</a>.</li>
<li>Controlled rollout of the resource via <a href="https://learn.microsoft.com/azure/kubernetes-fleet/howto-staged-update-run?pivots=cluster-scope" target="_blank" rel="noopener noreferrer">ClusterStagedUpdateRun</a>.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-1-create-the-mitigation-namespace-and-daemonset">Step 1: Create the mitigation Namespace and DaemonSet<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-1-create-the-mitigation-namespace-and-daemonset" class="hash-link" aria-label="Direct link to Step 1: Create the mitigation Namespace and DaemonSet" title="Direct link to Step 1: Create the mitigation Namespace and DaemonSet" translate="no">​</a></h3>
<p>Create the <code>01-kernel-lpe-mitigate.yaml</code> file with the following contents. The DaemonSet blocks vulnerable modules covered by the CVEs.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cve</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mitigate</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> DaemonSet</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mitigate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cve</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mitigate</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mitigate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">purpose</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> security</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mitigation</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mitigate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mitigate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">hostPID</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">priorityClassName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> system</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">critical</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">nodeSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">kubernetes.io/os</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> linux</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">tolerations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">operator</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Exists</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mitigate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mcr.microsoft.com/cbl</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mariner/busybox</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">2.0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /bin/sh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">c</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              echo "=== Kernel LPE Module Mitigation ==="</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              echo "Covers: CVE-2026-31431 (algif_aead), DirtyFrag (esp4/esp6/rxrpc)"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              MODULES="algif_aead esp4 esp6 rxrpc"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              for mod in $MODULES; do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                if </span><span class="token tag" style="color:#00009f">!</span><span class="token plain"> grep </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">qs "install $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">mod</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> /bin/false" /host/etc/modprobe.d/</span><span class="token important">*.conf</span><span class="token plain"> 2</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain">/dev/null; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  printf "install %s /bin/false\nblacklist %s\n" "$mod" "$mod" </span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"> /host/etc/modprobe.d/disable</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe.conf</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  echo "Blocked $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">mod</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  echo "$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">mod</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> already blocked"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                fi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                if chroot /host grep </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">q "^$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">mod</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> " /proc/modules 2</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain">/dev/null; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  if chroot /host modprobe </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">r "$mod" 2</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain">/dev/null; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    echo "Unloaded $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">mod</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token key atrule" style="color:#00a4db">echo "WARNING</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Could not unload $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">mod</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> (in use). Reboot node."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  fi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                fi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              done</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              echo "=== Mitigation complete. Sleeping ==="</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              sleep infinity</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 10m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 16Mi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 32Mi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">securityContext</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">privileged</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">volumeMounts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> host</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">root</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">mountPath</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /host</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> host</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">root</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">hostPath</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Directory</span><br></span></code></pre></div></div>
<p>Stage the mitigation namespace and DaemonSet on the Fleet Manager hub cluster.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f 01-kernel-lpe-mitigate.yaml</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>The DaemonSet is not instantiated (0 pods are scheduled) on the Fleet Manager hub cluster. This is expected behavior.</p></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-2-create-a-clusterresourceplacement">Step 2: Create a ClusterResourcePlacement<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-2-create-a-clusterresourceplacement" class="hash-link" aria-label="Direct link to Step 2: Create a ClusterResourcePlacement" title="Direct link to Step 2: Create a ClusterResourcePlacement" translate="no">​</a></h3>
<p>Create a placement manifest <code>02-fleet-pick-clusters-mitigation.yaml</code> that selects all Fleet Manager member clusters (<code>PickAll</code>) as suitable to receive the Namespace and its DaemonSet. We set the strategy to <code>External</code> so we can define and control the rollout order for clusters, which we will do in following steps.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> placement.kubernetes</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">fleet.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ClusterResourcePlacement</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cve</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mit</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ns</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">place</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">resourceSelectors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">group</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cve</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mitigate</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">policy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">placementType</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> PickAll</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">strategy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> External</span><br></span></code></pre></div></div>
<p>Apply the placement on Fleet Manager hub cluster.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f 02-fleet-pick-clusters-mitigation.yaml</span><br></span></code></pre></div></div>
<p>You can validate how many clusters will receive the mitigation using this command.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get clusterresourceplacement kernel-lpe-cve-mit-ns-place -o jsonpath="{.status.conditions[?(@.type=='ClusterResourcePlacementScheduled')].message}"</span><br></span></code></pre></div></div>
<p>The message in the response shows how many clusters were selected.</p>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">found all cluster needed as specified by the scheduling policy, found 2 cluster(s)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-3-create-a-clusterstagedupdatestrategy">Step 3: Create a ClusterStagedUpdateStrategy<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-3-create-a-clusterstagedupdatestrategy" class="hash-link" aria-label="Direct link to Step 3: Create a ClusterStagedUpdateStrategy" title="Direct link to Step 3: Create a ClusterStagedUpdateStrategy" translate="no">​</a></h3>
<p>A strategy is used to define the rollout order and soak duration per rollout stage. Note that all selected clusters must be in a stage. This is determined by the labels set of the cluster when it was added to the Fleet Manager.</p>
<p>In this sample <code>03-fleet-mitigation-rollout-strategy.yaml</code> we have three stages, with a 4 hour soak time between each stage. You can also add approvals if required.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> placement.kubernetes</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">fleet.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ClusterStagedUpdateStrategy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cve</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mit</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ds</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">strategy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">stages</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> stage1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">upgroup</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> canary</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">afterStageTasks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> TimedWait</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">waitTime</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 4h</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">maxConcurrency</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> stage2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">upgroup</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nonprod</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">afterStageTasks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> TimedWait</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">waitTime</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 4h</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">maxConcurrency</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> stage3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">upgroup</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> prod</span><br></span></code></pre></div></div>
<p>Apply the strategy on Fleet Manager hub cluster.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f 03-fleet-mitigation-rollout-strategy.yaml</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-4-start-rollout">Step 4: Start rollout<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-4-start-rollout" class="hash-link" aria-label="Direct link to Step 4: Start rollout" title="Direct link to Step 4: Start rollout" translate="no">​</a></h3>
<p>Finally, create a ClusterStagedUpdateRun <code>04-fleet-mitigation-rollout-run.yaml</code> to begin the rollout.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>To start the rollout later, set <code>state</code> to <code>Initialize</code>. You can patch the resource to <code>Run</code> to begin rollout (see below).</p></div></div>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> placement.kubernetes</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">fleet.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ClusterStagedUpdateRun</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cve</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mit</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ds</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">rollout</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">placementName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cve</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mit</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ns</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">place</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">stagedRolloutStrategyName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kernel</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">lpe</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cve</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mit</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ds</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">strategy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">state</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Run</span><br></span></code></pre></div></div>
<p>Apply the update run on Fleet Manager hub cluster. If you have <code>Run</code> as the state, the rollout begins.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f 04-fleet-mitigation-rollout-run.yaml</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-5-monitor-and-control-rollout">Step 5: Monitor and control rollout<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-5-monitor-and-control-rollout" class="hash-link" aria-label="Direct link to Step 5: Monitor and control rollout" title="Direct link to Step 5: Monitor and control rollout" translate="no">​</a></h3>
<p>Use the following command to retrieve the status of the rollout. Inspect the <code>conditions</code> and <code>stageStatuses</code> arrays to review progress.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get clusterstagedupdaterun kernel-lpe-cve-mit-ds-rollout -o yaml</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>You can also use the preview version of <a href="https://github.com/kubefleet-dev/kubefleet-headlamp-plugin" target="_blank" rel="noopener noreferrer">KubeFleet's Headlamp plugin</a> to help monitor and control the rollout. Connect it to the Fleet Manager hub cluster to use.</p></div></div>
<p>If you want to stop the rollout, you can patch the update run:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl patch clusterstagedupdaterun kernel-lpe-cve-mit-ds-rollout --type merge -p '{"spec":{"state":"Stop"}}'</span><br></span></code></pre></div></div>
<p>Restart the rollout by patching the update run:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl patch clusterstagedupdaterun kernel-lpe-cve-mit-ds-rollout --type merge -p '{"spec":{"state":"Run"}}'</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-6-validate-mitigation-is-applied">Step 6: Validate mitigation is applied<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-6-validate-mitigation-is-applied" class="hash-link" aria-label="Direct link to Step 6: Validate mitigation is applied" title="Direct link to Step 6: Validate mitigation is applied" translate="no">​</a></h3>
<p>Select clusters in your fleet that have received the mitigation and run the following check.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs -n kernel-lpe-cve-mitigate-ns -l app=kernel-lpe-mitigate</span><br></span></code></pre></div></div>
<p>If the mitigation is applied successfully you should see.</p>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">=== Kernel LPE Module Mitigation ===</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Covers: CVE-2026-31431 (algif_aead), DirtyFrag (esp4/esp6/rxrpc)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Blocked algif_aead</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Blocked esp4 </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Blocked esp6</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Blocked rxrpc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">=== Mitigation complete. Sleeping ===</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>In some cases where the module is in use, the node will require a reboot. The log message will indicate this is required.</p></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-7-roll-back-or-remove">Step 7: Roll back or remove<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-7-roll-back-or-remove" class="hash-link" aria-label="Direct link to Step 7: Roll back or remove" title="Direct link to Step 7: Roll back or remove" translate="no">​</a></h3>
<p>If issues occur, or the DaemonSet is no longer required as the node image has been updated to a permanently patched release, you can remove the mitigation DaemonSet by deleting the ClusterResourcePlacement on the Fleet Manager hub cluster.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl delete clusterresourceplacement kernel-lpe-cve-mit-ns-place</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="daemonset-rollout-outcome">DaemonSet Rollout Outcome<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#daemonset-rollout-outcome" class="hash-link" aria-label="Direct link to DaemonSet Rollout Outcome" title="Direct link to DaemonSet Rollout Outcome" translate="no">​</a></h3>
<ul>
<li>Immediate mitigation applied across clusters via DaemonSet.</li>
<li>Controlled rollout reduces risk, allows testing on clusters in batches.</li>
<li>No node reboot required when module not in use.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="permanent-mitigation---node-image-upgrade">Permanent mitigation - node image upgrade<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#permanent-mitigation---node-image-upgrade" class="hash-link" aria-label="Direct link to Permanent mitigation - node image upgrade" title="Direct link to Permanent mitigation - node image upgrade" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-important admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>important</div><div class="admonitionContent_BuS1"><ul>
<li>
<p>Make sure to review the steps in <a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#before-you-begin">before you begin</a>, especially adding update groups to member clusters.</p>
</li>
<li>
<p>Depending on the regional spread of your clusters, it is possible that the update run may pause for periods as it waits for the node image to be published to a region. This is expected behavior. You can apply the DaemonSet to your clusters to ensure they are protected while waiting for the updated node image to be available.</p>
</li>
</ul></div></div>
<p>Once AKS provides a permanent mitigation via updated node images you can use Fleet Manager update runs to update the node image across your clusters.</p>
<p>Fleet Manager's <a href="https://learn.microsoft.com/azure/kubernetes-fleet/concepts-update-orchestration" target="_blank" rel="noopener noreferrer">safe multi-cluster updates</a> feature enables:</p>
<ul>
<li>Application of consistent node image across clusters using the <a href="https://learn.microsoft.com/azure/kubernetes-fleet/concepts-update-orchestration#nodeimage-channel" target="_blank" rel="noopener noreferrer">Node Image channel</a>.</li>
<li>Controlled rollout of the node image update via <a href="https://learn.microsoft.com/azure/kubernetes-fleet/update-create-update-strategy?tabs=azure-portal" target="_blank" rel="noopener noreferrer">Update Strategies</a>.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-1-define-an-update-strategy">Step 1: Define an update strategy<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-1-define-an-update-strategy" class="hash-link" aria-label="Direct link to Step 1: Define an update strategy" title="Direct link to Step 1: Define an update strategy" translate="no">​</a></h3>
<p>An update strategy defines the order in which clusters will receive the node image update. Strategies are defined using a simple JSON format as shown next.</p>
<p>In the following sample we have one group of clusters per stage, with clusters grouped based on the <code>update-group</code> on the member cluster. We continue with the 4 hour soak time and have a 100% concurrency which allows up to 50 clusters to update at once. You can also set these up via the Azure portal if desired.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"stages"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"stage-1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"maxConcurrency"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100%"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"groups"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"canary"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token property" style="color:#36acaa">"maxConcurrency"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100%"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"afterStageWaitInSeconds"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">14400</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"stage-2"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"maxConcurrency"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100%"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"groups"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"nonprod"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token property" style="color:#36acaa">"maxConcurrency"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100%"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"afterStageWaitInSeconds"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">14400</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"stage-3"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"maxConcurrency"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100%"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"groups"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"prod"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token property" style="color:#36acaa">"maxConcurrency"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100%"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>Save the strategy as a file (<code>nodeimage-strategy.json</code>) and then submit via the Azure CLI to create the Strategy ready for use in Fleet Manager.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export GROUP=resource-group</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export FLEET=fleet-name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export STRATEGY=copyfail-mitigation-strategy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az fleet updatestrategy create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --fleet-name $FLEET \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name $STRATEGY \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --stages nodeimage-strategy.json</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>To update a strategy after you have created it, make changes to the JSON file and simply call the <code>create</code> command again.</p></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-2-create-an-update-run-for-node-image-upgrades">Step 2: Create an update run for node image upgrades<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-2-create-an-update-run-for-node-image-upgrades" class="hash-link" aria-label="Direct link to Step 2: Create an update run for node image upgrades" title="Direct link to Step 2: Create an update run for node image upgrades" translate="no">​</a></h3>
<p>Next, let's create a new update run for updating just the node image on selected clusters.</p>
<p>The update run won't automatically start, so you can create this once AKS releases the patched node images, then start the run at a later time that suits your schedule.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az fleet updaterun create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --fleet-name $FLEET \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name copyfail-nodeimage-update \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --upgrade-type NodeImageOnly \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --update-strategy-name $STRATEGY</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-3-start-the-update-run">Step 3: Start the update run<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-3-start-the-update-run" class="hash-link" aria-label="Direct link to Step 3: Start the update run" title="Direct link to Step 3: Start the update run" translate="no">​</a></h3>
<p>Once you are satisfied that all clusters to be upgraded are included, you can start the update run.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az fleet updaterun start \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --fleet-name $FLEET \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name copyfail-nodeimage-update</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="step-4-monitor-progress">Step 4: Monitor progress<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#step-4-monitor-progress" class="hash-link" aria-label="Direct link to Step 4: Monitor progress" title="Direct link to Step 4: Monitor progress" translate="no">​</a></h3>
<p>You can review the status using the Azure CLI shown below, or use the <a href="https://learn.microsoft.com/azure/kubernetes-fleet/update-orchestration?tabs=azure-portal#manage-an-update-run" target="_blank" rel="noopener noreferrer">Azure portal's web interface</a> to view the rollout graphically.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az fleet updaterun show \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group $GROUP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --fleet-name $FLEET \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name copyfail-nodeimage-update</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="node-image-upgrade-outcome">Node Image Upgrade Outcome<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#node-image-upgrade-outcome" class="hash-link" aria-label="Direct link to Node Image Upgrade Outcome" title="Direct link to Node Image Upgrade Outcome" translate="no">​</a></h3>
<ul>
<li>All nodes are updated to the patched node image.</li>
<li>DaemonSet no longer required and can be removed.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="summary-and-next-steps">Summary and next steps<a href="https://blog.aks.azure.com/2026/05/11/kernel-lpe-cve-patching-at-scale-with-fleet-manager#summary-and-next-steps" class="hash-link" aria-label="Direct link to Summary and next steps" title="Direct link to Summary and next steps" translate="no">​</a></h2>
<p>In this post we've looked at the multiple ways in which Azure Kubernetes Fleet Manager can help you safely apply mitigations for the Copy Fail CVE across all your clusters.</p>
<p>Fleet Manager continues to add new capabilities regularly, with the goal of simplifying at-scale Kubernetes cluster management for everyone.</p>
<p>Here's some recommended next steps:</p>
<ul>
<li><a href="https://github.com/Azure/AKS/issues/5753" target="_blank" rel="noopener noreferrer">Review AKS Advisory and Mitigation for CVE-2026-31431 (Copy Fail)</a>.</li>
<li><a href="https://learn.microsoft.com/azure/kubernetes-fleet/concepts-update-orchestration" target="_blank" rel="noopener noreferrer">Learn more about Fleet Manager Safe Multi-cluster Updates</a>.</li>
<li><a href="https://learn.microsoft.com/azure/kubernetes-fleet/concepts-resource-placement?pivots=cluster-scope" target="_blank" rel="noopener noreferrer">Learn more about Fleet Manager resource placement</a>.</li>
<li><a href="https://aka.ms/kubernetes-fleet/roadmap" target="_blank" rel="noopener noreferrer">View the Fleet Manager public roadmap</a>. Feature requests always welcome!</li>
<li><a href="https://aka.ms/kubefleet/commcalls" target="_blank" rel="noopener noreferrer">Join a KubeFleet community call</a> to learn about Fleet Manager's open source resource placement.</li>
</ul>]]></content:encoded>
            <category>Security</category>
            <category>Azure Kubernetes Fleet Manager</category>
            <category>KubeFleet</category>
        </item>
        <item>
            <title><![CDATA[Securing Argo CD with Microsoft Entra ID: a step-by-step guide]]></title>
            <link>https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra</link>
            <guid>https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra</guid>
            <pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to secure the Argo CD extension for AKS with Microsoft Entra ID using Terraform, workload identity, OIDC, RBAC, and policy-based access control.]]></description>
            <content:encoded><![CDATA[<p>AKS has offered GitOps support for a while now with the <a href="https://learn.microsoft.com/azure/azure-arc/kubernetes/tutorial-use-gitops-flux2?tabs=azure-cli" target="_blank" rel="noopener noreferrer">Flux v2 cluster extension</a>, and many users have been asking for Argo CD support as well. A few weeks ago at KubeCon Europe 2026, the <a href="https://techcommunity.microsoft.com/blog/azurearcblog/announcing-public-preview-of-argo-cd-extension-on-aks-and-azure-arc-enabled-kube/4504497" target="_blank" rel="noopener noreferrer">public preview of Argo CD extension for AKS was announced</a>. Running Argo CD as an AKS extension means you can lean on Azure for enhanced security and operations: automatic patch releases so you're always up to date, reduced CVEs with images based on Azure Linux, easy single sign-on (SSO) with Microsoft Entra ID, and workload identity federation so Argo CD can authenticate to Azure services without managing secrets.</p>
<p>In this post, we'll focus on the identity side of that story. We'll walk through integrating Argo CD with Microsoft Entra ID to give your developers SSO access using their existing corporate identities; no separate passwords to manage. You also get the full power of Azure's identity platform: multi-factor authentication (MFA), conditional access policies, and group-based role assignments to control who can do what in Argo CD. We'll automate the entire setup with Terraform so the configuration is repeatable, version-controlled, and easy to adapt across environments.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="overview">Overview<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#overview" class="hash-link" aria-label="Direct link to Overview" title="Direct link to Overview" translate="no">​</a></h2>
<p>By now, <a href="https://argo-cd.readthedocs.io/en/stable/" target="_blank" rel="noopener noreferrer">Argo CD</a> probably needs no introduction. But if you are new to Argo CD, it is a declarative, GitOps continuous delivery tool for Kubernetes. It allows you to manage your Kubernetes applications using Git repositories as the source of truth.</p>
<p>Argo CD is heavily used in production environments and many have already adopted the open-source version of Argo CD. With the new Argo CD extension for AKS, you can now easily deploy the software on your cluster as an Azure-native resource. But what does that mean? Well, for one, it means you can now leverage Azure CLI, Azure Resource Manager (ARM) templates, Bicep, or Terraform to manage your Argo CD deployments as you bootstrap clusters. It also means you no longer need to worry about upgrading and patching Argo CD. Azure handles that for you, so you can focus on deploying applications.</p>
<p>By default, the Argo CD installation comes with a built-in admin user. However, for production environments, it is recommended to use an external identity provider for authentication and authorization. Microsoft Entra ID is a great choice for this as it provides robust security features and seamless integration with Azure services. My motto has always been "ditch the passwords" and using Microsoft Entra ID for authentication allows you to do just that. You can leverage features like multi-factor authentication (MFA), conditional access policies, and role-based access control (RBAC) to secure your Argo CD deployment.</p>
<p>At a high level, the process of securing Argo CD with Microsoft Entra ID involves the following steps:</p>
<ol>
<li>Create a Microsoft Entra ID application for Argo CD</li>
<li>Create OIDC configuration settings to connect Microsoft Entra ID and enable workload identity for Argo CD</li>
<li>Create policy mappings to grant Microsoft Entra ID users/groups access to Argo CD</li>
<li>Create an AKS cluster with OIDC issuer and workload identity enabled</li>
<li>Deploy the Argo CD extension for AKS</li>
<li>Verify the integration</li>
</ol>
<p>There's a bit of prerequisite setup to go through, especially when it comes to configuring Microsoft Entra ID applications and OIDC settings, but don't worry, we'll automate all of that using Terraform and I'll go through each step in detail. The end result will be a secure Argo CD deployment that is integrated with Microsoft Entra ID for authentication and authorization.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-0-prerequisites">Step 0: Prerequisites<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#step-0-prerequisites" class="hash-link" aria-label="Direct link to Step 0: Prerequisites" title="Direct link to Step 0: Prerequisites" translate="no">​</a></h2>
<p>Before you get started, make sure you have the following prerequisites in place:</p>
<ul>
<li><a href="https://aka.ms/azure-free-account" target="_blank" rel="noopener noreferrer">Azure subscription</a></li>
<li><a href="https://learn.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest" target="_blank" rel="noopener noreferrer">Azure CLI</a></li>
<li><a href="https://kubernetes.io/docs/reference/kubectl/" target="_blank" rel="noopener noreferrer">kubectl</a></li>
<li><a href="https://developer.hashicorp.com/terraform/install" target="_blank" rel="noopener noreferrer">Terraform</a></li>
<li><a href="https://argo-cd.readthedocs.io/en/stable/cli_installation/" target="_blank" rel="noopener noreferrer">Argo CD CLI (optional)</a></li>
</ul>
<p>Within your Azure tenant/subscription, you will also need to have the necessary permissions to create Microsoft Entra ID applications and manage Azure resources.</p>
<p>Unless you are the Global Administrator in your tenant, you will need to have one of the following roles to be able to create Microsoft Entra ID applications:</p>
<ul>
<li><a href="https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#application-administrator" target="_blank" rel="noopener noreferrer">Application Administrator</a></li>
<li><a href="https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#application-developer" target="_blank" rel="noopener noreferrer">Application Developer</a></li>
<li><a href="https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#cloud-application-administrator" target="_blank" rel="noopener noreferrer">Cloud Application Administrator</a></li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-1-terraform-provider-setup">Step 1: Terraform provider setup<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#step-1-terraform-provider-setup" class="hash-link" aria-label="Direct link to Step 1: Terraform provider setup" title="Direct link to Step 1: Terraform provider setup" translate="no">​</a></h2>
<p>Create a new directory for your Terraform configuration and navigate to it and create a new file named <code>main.tf</code>.</p>
<p>In this file, we will define the Terraform providers for <a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest" target="_blank" rel="noopener noreferrer">Azure Resource Manager (azurerm)</a> and <a href="https://registry.terraform.io/providers/hashicorp/azuread/latest" target="_blank" rel="noopener noreferrer">Microsoft Entra ID (azuread)</a>. The <code>azurerm</code> provider will allow us to manage Azure resources, while the <code>azuread</code> provider will allow us to manage Microsoft Entra ID applications and related resources.</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  required_providers {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azuread = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      source  = "hashicorp/azuread"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      version = "~&gt; 3.8.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    azurerm = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      source  = "hashicorp/azurerm"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      version = "~&gt; 4.69.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">provider "azurerm" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  features {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    resource_group {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      prevent_deletion_if_contains_resources = false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-2-create-microsoft-entra-id-application-for-argo-cd">Step 2: Create Microsoft Entra ID application for Argo CD<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#step-2-create-microsoft-entra-id-application-for-argo-cd" class="hash-link" aria-label="Direct link to Step 2: Create Microsoft Entra ID application for Argo CD" title="Direct link to Step 2: Create Microsoft Entra ID application for Argo CD" translate="no">​</a></h2>
<p>Next, let's create some data sources to query Microsoft Entra ID for the necessary information to set up our application and OIDC configuration. Add the following code to your <code>main.tf</code> file to get a list of well-known application IDs, the current client's configuration, and the service principal for Microsoft Graph.</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">data "azuread_application_published_app_ids" "well_known" {}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">data "azuread_client_config" "current" {}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">data "azuread_service_principal" "msgraph" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  client_id = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Since the OIDC configuration for Argo CD requires the IDs of specific Microsoft Graph scopes, we will also create a local variable named <code>msgraph_scopes</code> that includes all the scopes from the Microsoft Graph service principal. This will allow us to easily reference the scope IDs when we create our Microsoft Entra ID application for Argo CD.</p>
<p>Add the following code to your <code>main.tf</code> file to create the <code>msgraph_scopes</code> local variable:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">locals {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  msgraph_scopes = { for s in data.azuread_service_principal.msgraph.oauth2_permission_scopes : s.value =&gt; s }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Now we are ready to create our Microsoft Entra ID application for Argo CD. This application will represent your Argo CD in Microsoft Entra ID and will be used for authentication and authorization.</p>
<p>Add the following code to your <code>main.tf</code> file to create the application:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azuread_application" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  display_name            = "myargocdapp"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  owners                  = [data.azuread_client_config.current.object_id]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  sign_in_audience        = "AzureADMyOrg"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  group_membership_claims = ["ApplicationGroup"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  web {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    redirect_uris = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      "https://localhost:9000/auth/callback"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  public_client {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    redirect_uris = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      "http://localhost:8085/auth/callback"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  required_resource_access {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    resource_app_id = "00000003-0000-0000-c000-000000000000" # Microsoft Graph</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    resource_access {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      id   = local.msgraph_scopes["openid"].id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      type = "Scope"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    resource_access {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      id   = local.msgraph_scopes["profile"].id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      type = "Scope"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    resource_access {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      id   = local.msgraph_scopes["email"].id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      type = "Scope"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    resource_access {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      id   = local.msgraph_scopes["User.Read"].id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      type = "Scope"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional_claims {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    id_token {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      name      = "groups"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      essential = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Here's what each part of the application configuration does:</p>
<ul>
<li><strong><code>sign_in_audience = "AzureADMyOrg"</code></strong> — restricts sign-in to users in your Microsoft Entra ID tenant only</li>
<li><strong><code>group_membership_claims = ["ApplicationGroup"]</code></strong> — includes group membership in the token, which we'll use later for RBAC role mapping</li>
<li><strong><code>web</code> and <code>public_client</code> redirect URIs</strong> — required for the OIDC authentication flow with Argo CD (the <code>web</code> URI is for the browser-based flow, and the <code>public_client</code> URI is for the CLI). Since we're testing locally, we use localhost URIs here, but for production you'll want to update these to match your deployment</li>
<li><strong><code>required_resource_access</code></strong> — requests Microsoft Graph API permissions (<code>openid</code>, <code>profile</code>, <code>email</code>, and <code>User.Read</code>) needed for the OIDC flow and to retrieve user information</li>
<li><strong><code>optional_claims</code></strong> — includes the user's group memberships in the ID token, enabling group-based access control in Argo CD</li>
</ul>
<p>The workload identity integration requires Argo CD to exchange its Kubernetes service account token for a Microsoft Entra ID token, so we will need to create a federated identity credential for the application to establish trust between Microsoft Entra ID and the cluster's OIDC issuer.</p>
<p>Add the following code to your <code>main.tf</code> file to create the federated identity credential for the application:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azuread_application_federated_identity_credential" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  application_id = azuread_application.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  display_name   = azuread_application.example.display_name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  audiences      = ["api://AzureADTokenExchange"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  issuer         = azurerm_kubernetes_cluster.example.oidc_issuer_url</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  subject        = "system:serviceaccount:argocd:argocd-server"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Next, we need to create a service principal for the application. The service principal is what will be used to grant permissions and access to resources in Azure.</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azuread_service_principal" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  client_id = azuread_application.example.client_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Now we need to grant the service principal the necessary permissions to access Microsoft Graph on behalf of the user. Add the following code to your <code>main.tf</code> file to create the delegated permission grant:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azuread_service_principal_delegated_permission_grant" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  service_principal_object_id          = azuread_service_principal.example.object_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resource_service_principal_object_id = data.azuread_service_principal.msgraph.object_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  claim_values                         = ["openid", "profile", "email", "User.Read"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>The permission grant is optional. Without it, users will be asked to consent to the permissions. By creating this permission grant, we are pre-consenting on behalf of the users in our tenant so that they won't see the consent prompt when they sign in to Argo CD.</p></div></div>
<p>With the service principal and permissions in place, we can now grant Microsoft Entra ID users and groups access to Argo CD by creating app role assignments. Add the following code to your <code>main.tf</code> file to retrieve a Microsoft Entra ID group object ID.</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">data "azuread_group" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  display_name = "Argo CD Admins" # Update this to match your group name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>You might already have an existing group in Microsoft Entra ID that you want to use for Argo CD access. If so, update the <code>display_name</code> in the <code>azuread_group</code> data source above to match the name of your group. If not, create a new group in Microsoft Entra ID and add your user accounts to that group and then update the <code>display_name</code> accordingly.</p></div></div>
<p>Finally, add the following code to your <code>main.tf</code> file to allow members of the Microsoft Entra ID group to have access to the Argo CD application.</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azuread_app_role_assignment" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  app_role_id         = "00000000-0000-0000-0000-000000000000" # Default app role</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  principal_object_id = data.azuread_group.example.object_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resource_object_id  = azuread_service_principal.example.object_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-3-create-aks-cluster">Step 3: Create AKS cluster<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#step-3-create-aks-cluster" class="hash-link" aria-label="Direct link to Step 3: Create AKS cluster" title="Direct link to Step 3: Create AKS cluster" translate="no">​</a></h2>
<p>Now comes the easy part, creating the AKS cluster 😎</p>
<p>Add the following code to your <code>main.tf</code> file to create a resource group:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_resource_group" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name     = "myresourcegroup"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  location = "westus3" # Update to your preferred region</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Add the following code to your <code>main.tf</code> file to create an AKS cluster with OIDC issuer and workload identity enabled:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_kubernetes_cluster" "example" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name                      = "myakscluster"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  location                  = azurerm_resource_group.example.location</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resource_group_name       = azurerm_resource_group.example.name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  dns_prefix                = "myakscluster"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  oidc_issuer_enabled       = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  workload_identity_enabled = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  default_node_pool {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name       = "default"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    node_count = 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  identity {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    type = "SystemAssigned"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_BuS1"><p>This sample configuration creates a very basic AKS standard cluster. It is crucial to enable the OIDC issuer and workload identity for the integration with Microsoft Entra ID as these features allow the cluster to issue OIDC tokens that Argo CD pods can use to exchange with Microsoft Entra ID for authentication and authorization. You could also choose to deploy an AKS Automatic cluster instead which will have the OIDC issuer and workload identity enabled by default.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-4-deploy-argo-cd-extension-for-aks">Step 4: Deploy Argo CD extension for AKS<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#step-4-deploy-argo-cd-extension-for-aks" class="hash-link" aria-label="Direct link to Step 4: Deploy Argo CD extension for AKS" title="Direct link to Step 4: Deploy Argo CD extension for AKS" translate="no">​</a></h2>
<p>Okay, the moment we've all been waiting for, deploying the Argo CD extension for AKS!</p>
<p>Before we can deploy the extension, we need to create the necessary configuration settings for the OIDC integration with Microsoft Entra ID. This includes the OIDC configuration and the RBAC policy mappings.</p>
<p>Add the following code to your <code>main.tf</code> file to create more local variables for the OIDC configuration and RBAC policy mappings:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">locals {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  oidc_config = &lt;&lt;EOT</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">name: Microsoft Entra ID</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">issuer: https://login.microsoftonline.com/${data.azuread_client_config.current.tenant_id}/v2.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">clientID: ${azuread_application.example.client_id}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">azure:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  useWorkloadIdentity: true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">requestedIDTokenClaims:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  groups:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    essential: true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">requestedScopes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - openid</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - profile</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - email</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOT</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  policy_csv = &lt;&lt;EOT</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">g, "${data.azuread_group.example.object_id}", role:admin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOT</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>This OIDC configuration specifies the issuer URL for Microsoft Entra ID, the client ID of our application, and the requested scopes and claims. The RBAC policy mapping specifies that members of the Microsoft Entra ID group we referenced earlier will be assigned the "admin" role in Argo CD.</p>
<p>With the configuration settings in place, we can now deploy the Argo CD extension for AKS. The nice thing here is that even though the Argo CD extension is in public preview, we can deploy it using the <code>azurerm_kubernetes_cluster_extension</code> resource in Terraform since the underlying call to Azure Resource Manager does not require any special handling for preview extensions.</p>
<p>Add the following code to your <code>main.tf</code> file to deploy the Argo CD extension for AKS:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "azurerm_kubernetes_cluster_extension" "argocd" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name           = "argocd"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  cluster_id     = azurerm_kubernetes_cluster.example.id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  extension_type = "Microsoft.ArgoCD"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  release_train  = "Preview"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  configuration_settings = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "azure.workloadIdentity.enabled"          = "true"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "azure.workloadIdentity.clientId"         = azuread_application.example.client_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "azure.workloadIdentity.entraSSOClientId" = azuread_application.example.client_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "redis-ha.enabled"                        = "false"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "global.domain"                           = "localhost:9000"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "configs.cm.admin\\.enabled"              = "false"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "configs.cm.oidc\\.config"                = local.oidc_config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "configs.rbac.policy\\.csv"               = local.policy_csv</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>This extension is primarily based on the <a href="https://github.com/argoproj/argo-helm/tree/main/charts/argo-cd" target="_blank" rel="noopener noreferrer">open-source Argo CD Helm chart</a>, so the configuration settings follow a similar pattern to Helm values with some Azure-specific additions. Here's a quick breakdown:</p><ul>
<li><strong><code>azure.workloadIdentity.*</code></strong> — convenience settings that wire up the workload identity integration for you</li>
<li><strong><code>configs.cm.admin.enabled = "false"</code></strong> — disables the built-in admin user since we're using Microsoft Entra ID</li>
<li><strong><code>global.domain = "localhost:9000"</code></strong> — sets the domain Argo CD uses for redirect URLs (update this for production)</li>
<li><strong><code>configs.cm.oidc.config</code></strong> and <strong><code>configs.rbac.policy.csv</code></strong> — the OIDC and RBAC settings we defined earlier</li>
</ul></div></div>
<p>You might be asking, what about the version of the Argo CD extension? Since this extension is managed by Azure, we can omit the version and let Azure deploy the latest version and automatically upgrade it when new versions are available. This is one of the benefits of using an Azure-managed extension as it reduces the operational overhead of managing the extension and ensures that you are always running a secure and up-to-date version.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-5-run-terraform-and-verify">Step 5: Run Terraform and verify<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#step-5-run-terraform-and-verify" class="hash-link" aria-label="Direct link to Step 5: Run Terraform and verify" title="Direct link to Step 5: Run Terraform and verify" translate="no">​</a></h2>
<p>We're nearly there! The last step is to add some output values to our <code>main.tf</code> file so that we can easily retrieve the resource group name and AKS cluster name after the deployment. Then we'll run <code>terraform init</code> and <code>terraform apply</code> to create all the resources. Once the deployment is complete, we can retrieve the credentials for our AKS cluster and verify that we can sign in to Argo CD using Microsoft Entra ID.</p>
<p>Add the following code to your <code>main.tf</code> file to create the output values:</p>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">output "rg_name" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  description = "The name of the Resource Group"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  value       = azurerm_resource_group.example.name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">output "aks_name" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  description = "The name of the AKS cluster"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  value       = azurerm_kubernetes_cluster.example.name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Open a terminal, navigate to the directory containing your <code>main.tf</code> file, and run the following commands:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform init</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">terraform apply</span><br></span></code></pre></div></div>
<p>The <code>terraform init</code> command will initialize the Terraform working directory and download the necessary providers, while the <code>terraform apply</code> command will create all the resources defined in your <code>main.tf</code> file. Review the plan output and type <code>yes</code> to proceed with the deployment.</p>
<p>Once the deployment is complete, run the following command to retrieve the credentials for your AKS cluster:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az aks get-credentials --resource-group $(terraform output -raw rg_name) --name $(terraform output -raw aks_name)</span><br></span></code></pre></div></div>
<p>Now that we have the credentials for our AKS cluster, we can verify that we can sign in to Argo CD using Microsoft Entra ID. Since we set the <code>global.domain</code> to <code>localhost:9000</code>, we will need to port-forward the Argo CD server to access it locally. Run the following command to port-forward the Argo CD server:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl port-forward svc/argocd-server -n argocd 9000:443</span><br></span></code></pre></div></div>
<p>Open a web browser and navigate to <a href="https://localhost:9000/" target="_blank" rel="noopener noreferrer">https://localhost:9000</a>. You should be presented with a warning about an untrusted certificate. This is expected since we're using a self-signed certificate for local testing. Proceed past the warning to see the Argo CD sign-in page. Select the <strong>Log in via Microsoft Entra ID</strong> button and single sign-on (SSO) should kick in.</p>
<p>Navigate to the "User Info" page and you should see your Microsoft Entra ID user information along with the group claims.</p>
<p>If you have the Argo CD CLI installed, you can also verify the sign-in and view user information from the command line as well.</p>
<p>Keep the port-forwarding session running and open a new terminal window, then run the following command to sign in to Argo CD using SSO:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">argocd login localhost:9000 --sso --insecure</span><br></span></code></pre></div></div>
<p>You should see a browser window open to complete the authentication flow. After you sign in, you can run the following command to view your user information:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">argocd account get-user-info</span><br></span></code></pre></div></div>
<p>You will see the same user information and group claims as you saw in the web UI, confirming that the OIDC integration with Microsoft Entra ID is working correctly.</p>
<p>Congratulations! You have successfully secured Argo CD with Microsoft Entra ID for authentication and authorization. You can now manage your Kubernetes applications using Argo CD with the confidence that your deployment is secure and integrated with Azure services.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Going to production?</div><div class="admonitionContent_BuS1"><p>This walkthrough uses localhost URLs for local testing. Before deploying to production, make sure to:</p><ul>
<li>Update the <code>web</code> redirect URI in the Entra ID app registration and <code>global.domain</code> in the extension config to your real domain</li>
<li>Set up an Ingress controller with a valid TLS certificate</li>
<li>Consider adding granular RBAC roles beyond <code>role:admin</code> (for example, <code>role:readonly</code> for dev teams)</li>
<li>Review the <a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/" target="_blank" rel="noopener noreferrer">Argo CD RBAC documentation</a> for fine-grained policy options</li>
</ul></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="cleanup">Cleanup<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#cleanup" class="hash-link" aria-label="Direct link to Cleanup" title="Direct link to Cleanup" translate="no">​</a></h2>
<p>When you're done experimenting, you can tear down all the resources created in this walkthrough by running:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform destroy</span><br></span></code></pre></div></div>
<p>Review the plan output and type <code>yes</code> to confirm. This will remove the AKS cluster, Argo CD extension, Microsoft Entra ID application, and all related resources.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="summary">Summary<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<p>In this post, we walked through how to secure Argo CD with Microsoft Entra ID for authentication and authorization. We covered the steps to create a Microsoft Entra ID application for Argo CD, configure OIDC settings, create RBAC policy mappings, deploy the Argo CD extension for AKS, and verify the integration. By leveraging Microsoft Entra ID, you can enhance the security of your Argo CD deployment and take advantage of Azure's identity and access management features.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="whats-next">What's next?<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#whats-next" class="hash-link" aria-label="Direct link to What's next?" title="Direct link to What's next?" translate="no">​</a></h3>
<ul>
<li>Expose Argo CD with an Ingress controller and a real TLS certificate</li>
<li>Deploy a sample GitOps application through Argo CD to see it in action</li>
<li>Add granular RBAC roles for different teams in your organization</li>
<li>Explore the <a href="https://learn.microsoft.com/azure/azure-arc/kubernetes/tutorial-use-gitops-argocd" target="_blank" rel="noopener noreferrer">Argo CD extension docs</a> for more tutorials</li>
</ul>
<p>Tried this out? We'd love to hear how it went — drop us feedback in <a href="https://github.com/Azure/AKS/discussions" target="_blank" rel="noopener noreferrer">GitHub Discussions</a> or open an <a href="https://github.com/Azure/AKS/issues/new" target="_blank" rel="noopener noreferrer">issue</a> if you run into anything.</p>
<p>Happy deploying!</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="resources">Resources<a href="https://blog.aks.azure.com/2026/04/22/argocd-extension-with-microsoft-entra#resources" class="hash-link" aria-label="Direct link to Resources" title="Direct link to Resources" translate="no">​</a></h2>
<ul>
<li><a href="https://gist.github.com/pauldotyu/8d54d73300669efa1296e56f13e9e27d" target="_blank" rel="noopener noreferrer">Full Terraform code for this walkthrough</a></li>
<li><a href="https://techcommunity.microsoft.com/blog/azurearcblog/announcing-public-preview-of-argo-cd-extension-on-aks-and-azure-arc-enabled-kube/4504497" target="_blank" rel="noopener noreferrer">Announcing public preview of Argo CD extension on AKS and Azure Arc-enabled Kubernetes - Microsoft Tech Community</a></li>
<li><a href="https://argo-cd.readthedocs.io/en/stable/" target="_blank" rel="noopener noreferrer">Argo CD documentation</a></li>
<li><a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/user-management/microsoft/" target="_blank" rel="noopener noreferrer">Argo CD Microsoft Entra ID integration</a></li>
<li><a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/" target="_blank" rel="noopener noreferrer">Argo CD RBAC documentation</a></li>
<li><a href="https://learn.microsoft.com/azure/aks/workload-identity-overview" target="_blank" rel="noopener noreferrer">Workload identity for AKS</a></li>
<li><a href="https://learn.microsoft.com/azure/developer/terraform/overview" target="_blank" rel="noopener noreferrer">Terraform on Azure</a></li>
</ul>]]></content:encoded>
            <category>Argo CD</category>
            <category>Entra</category>
            <category>Extensions</category>
            <category>Security</category>
            <category>Best Practices</category>
            <category>Terraform</category>
        </item>
        <item>
            <title><![CDATA[Control AI spend with per-application token rate limiting using Application Network and agentgateway]]></title>
            <link>https://blog.aks.azure.com/2026/04/17/appnet-agentgateway</link>
            <guid>https://blog.aks.azure.com/2026/04/17/appnet-agentgateway</guid>
            <pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Use Application Network identity and agentgateway to enforce per-application token rate limiting and control shared AI service spend on AKS.]]></description>
            <content:encoded><![CDATA[<p>As organizations scale AI adoption, platform teams must balance two competing goals:</p>
<ul>
<li>Enable broad, low-friction access to AI services</li>
<li>Prevent a single application from exhausting shared quotas</li>
</ul>
<p>This article describes a <strong>platform-oriented approach</strong> to controlling AI spend using <strong>Azure Kubernetes Application Network</strong> (AppNet) and <strong>agentgateway</strong>. By leveraging <strong>workload identity already present in the network</strong>, you can enforce <strong>per-application, token-based rate limiting</strong> without issuing API keys to every application.</p>
<p>By adopting this platform-oriented approach, you gain centralized control over AI spending, eliminate secrets distribution, and improve operational efficiency.</p>
<p><strong>Azure Kubernetes Application Network</strong> (currently in Public Preview) is Azure's fully managed L7 network for AKS, providing security, observability, and control for your L7 network out of the box. This article focuses on AppNet's secure, automatic mTLS authentication.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-challenge-with-shared-ai-quotas">The challenge with shared AI quotas<a href="https://blog.aks.azure.com/2026/04/17/appnet-agentgateway#the-challenge-with-shared-ai-quotas" class="hash-link" aria-label="Direct link to The challenge with shared AI quotas" title="Direct link to The challenge with shared AI quotas" translate="no">​</a></h2>
<p>A typical AI inference workflow looks like this:</p>
<ol>
<li>Create an AI or model provider account.</li>
<li>Distribute a shared API key.</li>
<li>Allow applications to call the service.</li>
</ol>
<p>This approach works initially, but as usage grows, teams often encounter a shared failure mode:</p>
<blockquote>
<p><strong>403: Insufficient Quota</strong></p>
</blockquote>
<p>When the quota is exhausted:</p>
<ul>
<li>All applications are affected simultaneously.</li>
<li>It is difficult to identify which workload consumed the quota.</li>
<li>One misbehaving application can impact the entire platform.</li>
</ul>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-per-application-api-keys-dont-scale">Why per-application API keys don’t scale<a href="https://blog.aks.azure.com/2026/04/17/appnet-agentgateway#why-per-application-api-keys-dont-scale" class="hash-link" aria-label="Direct link to Why per-application API keys don’t scale" title="Direct link to Why per-application API keys don’t scale" translate="no">​</a></h2>
<p>A common mitigation is to issue one API key per application, each with an independent quota. While this improves attribution, it introduces new operational challenges:</p>
<ul>
<li>Manual key provisioning by platform engineers</li>
<li>Secret distribution and rotation</li>
<li>Slower onboarding for new applications</li>
<li>Increased operational overhead</li>
</ul>
<p>Instead of removing blockers, the platform becomes one.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="a-platform-oriented-solution">A platform-oriented solution<a href="https://blog.aks.azure.com/2026/04/17/appnet-agentgateway#a-platform-oriented-solution" class="hash-link" aria-label="Direct link to A platform-oriented solution" title="Direct link to A platform-oriented solution" translate="no">​</a></h2>
<p>A more scalable approach is to shift rate limiting out of application code and into the <strong>platform layer</strong>.</p>
<p><strong>The key idea is: Identify applications by identity, not by secrets.</strong></p>
<p>Azure Kubernetes Application Network automatically establishes workload identity through mutual TLS (mTLS) using Istio's ztunnel proxy. By enforcing policy based on this identity, the platform can apply per-application limits transparently.</p>
<p>This solution combines AppNet's automatic mTLS identity on all traffic with agentgateway's ability to define Token Rate Limiting buckets with CEL expressions to accomplish per-application Token budgets. By configuring agentgateway to terminate mTLS directly, we're able to have it participate in the network just like an Istio waypoint would, which gives it direct access to the TLS identity on the wire.</p>
<!-- -->
<p>In contrast with our initial scenario, the Azure Foundry API Key is only accessible to the
agentgateway, so application teams don't touch any secrets, while AppNet provides
per-application identity information on the wire.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="deep-dive-configure-application-network-and-agentgateway-for-token-based-rate-limiting">Deep Dive: Configure <strong>Application Network</strong> and <strong>agentgateway</strong> for token-based rate limiting<a href="https://blog.aks.azure.com/2026/04/17/appnet-agentgateway#deep-dive-configure-application-network-and-agentgateway-for-token-based-rate-limiting" class="hash-link" aria-label="Direct link to deep-dive-configure-application-network-and-agentgateway-for-token-based-rate-limiting" title="Direct link to deep-dive-configure-application-network-and-agentgateway-for-token-based-rate-limiting" translate="no">​</a></h2>
<p>Let's look at the key components that make up our rate limit.
For step-by-step setup details, see the <a href="https://gist.github.com/therealmitchconnors/b2776cea7a72e25f805b0228eef986cc#file-details-md" target="_blank" rel="noopener noreferrer">detailed configuration gist</a>.
First, let's configure agentgateway to interoperate with AppNet, which exposes an
Istio-compliant control plane:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> agentgateway.dev/v1alpha1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> AgentgatewayParameters</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> agentgateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">deployment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">istio.io/dataplane-mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> none</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">networking.istio.io/tunnel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">istio</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">rawConfig</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">binds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">listeners</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">15008</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">tunnelProtocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hboneGateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">service</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hbone</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">15008</span><br></span></code></pre></div></div>
<p>Next, let's create a policy to perform token-based rate limiting on this gateway's traffic, using the AppNet mTLS identity as the bucket key.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> agentgateway.dev/v1alpha1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> AgentgatewayPolicy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> minute</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">token</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">budget</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">targetRefs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">group</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway.networking.k8s.io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">traffic</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">rateLimit</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">global</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">backendRef</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">group</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ratelimit</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ratelimit</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8081</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">descriptors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">entries</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">expression</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> source.identity.namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> client_ns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">unit</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Tokens</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">domain</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> token</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">budgets</span><br></span></code></pre></div></div>
<p>Finally, let's configure our Rate Limiting Server to deny traffic after 100 tokens per application per minute (in reality, we'd need a much bigger budget, but this low budget lets us easily demo exceeding the rate limiter).</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">config.yaml</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">    domain: token-budgets</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">    descriptors:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">      # Rate limit by client namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">      - key: client_ns</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">        rate_limit:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          unit: minute</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          requests_per_unit: 100</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ConfigMap</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ratelimit</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">config</span><br></span></code></pre></div></div>
<p>Now that we've configured our rate limiter, let's send some completion requests to Azure Foundry to see it in action. Full test instructions are available in the <a href="https://gist.github.com/therealmitchconnors/b2776cea7a72e25f805b0228eef986cc#file-details-md" target="_blank" rel="noopener noreferrer">details gist</a>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">curl gateway.default/v1/chat/completions -i -H content-type:application/json  -d '{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   "model": "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   "messages": [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       "role": "system",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       "content": "You are a helpful assistant."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       "role": "user",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       "content": "Write a short haiku about cloud computing."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> }'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">HTTP/1.1 200 OK</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...(succeeds several times)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">curl gateway.default/v1/chat/completions -i -H content-type:application/json  -d '{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   "model": "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   "messages": [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       "role": "system",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       "content": "You are a helpful assistant."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       "role": "user",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       "content": "Write a short haiku about cloud computing."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> }'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">HTTP/1.1 429 Too Many Requests</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">content-length: 0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">date: Fri, 03 Apr 2026 22:59:18 GMT</span><br></span></code></pre></div></div>
<p>Once we've exhausted our token budget, all requests from httpbin to Azure Foundry will be
blocked by agentgateway until our budget resets in 1 minute. Requests from other
applications can proceed without being blocked, because each application has its own rate
limit bucket, keyed on AppNet identity.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://blog.aks.azure.com/2026/04/17/appnet-agentgateway#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>By adopting this platform-oriented approach, you gain centralized control over AI spending,
eliminate secrets distribution, and improve operational efficiency. Applications gain
transparent rate limiting without code changes, while platform teams reduce overhead and
enforce fair resource allocation across the organization. This is just one of the many ways
you can benefit from Application Network, built on Istio's Ambient Mode, with readily
available open source tools like agentgateway. To learn more, see <a href="https://learn.microsoft.com/azure/application-network/overview" target="_blank" rel="noopener noreferrer">Application Network
documentation</a> and
<a href="https://agentgateway.dev/" target="_blank" rel="noopener noreferrer">agentgateway documentation</a>.</p>]]></content:encoded>
            <category>Application Network</category>
            <category>AI</category>
        </item>
        <item>
            <title><![CDATA[AI Inference on AKS enabled by Azure Arc: Generative AI using Triton and TensorRT‑LLM]]></title>
            <link>https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5</link>
            <guid>https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5</guid>
            <pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Run local edge generative AI by deploying Triton with the TensorRT-LLM backend on AKS enabled by Azure Arc to serve Qwen2.5 7B Instruct with GPU acceleration.]]></description>
            <content:encoded><![CDATA[<p>In this post, you’ll deploy NVIDIA Triton Inference Server on your Azure Kubernetes Service (AKS) enabled by Azure Arc cluster to serve a Qwen‑based generative model using the TensorRT‑LLM backend. By the end, you’ll have a working generative AI inference pipeline running locally on your on‑premises GPU hardware.</p>
<p><img decoding="async" loading="lazy" alt="Deploying generative AI inference with open-source inference servers on AKS enabled by Azure Arc" src="https://blog.aks.azure.com/assets/images/hero-image-ccdd9a0aa85c1af434a106a2984267aa.png" width="650" height="467" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>Earlier parts of this series used runtimes such as Ollama and vLLM to quickly enable local LLM inference and iterate on model behavior. In this post, you move to a more infrastructure‑centric deployment model using <strong>NVIDIA Triton Inference Server with the TensorRT‑LLM backend</strong> and an instruction‑tuned model. This approach prioritizes <strong>predictable performance, efficient GPU utilization, and long‑running inference services</strong>, making it well suited for edge and on‑prem environments where capacity is fixed and inference must operate reliably as part of the platform.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>PREREQUISITES AND SCOPE</div><div class="admonitionContent_BuS1"><p>Before you begin, ensure the <a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2">Part 2 prerequisites</a> are met, including a GPU node configured for <strong>nvidia.com/gpu</strong>. Cluster nodes need <strong>internet access</strong> to download models. <strong>Expect a delay</strong> during initial deployment while the pod downloads and caches model files.</p><p>This tutorial is designed for experimentation and learning. The configurations shown are not production-ready and should not be deployed to production environments without additional security, reliability, performance hardening, and following standard practices.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="tensorrtllm-deployment-pipeline-on-triton">TensorRT‑LLM deployment pipeline on Triton<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#tensorrtllm-deployment-pipeline-on-triton" class="hash-link" aria-label="Direct link to TensorRT‑LLM deployment pipeline on Triton" title="Direct link to TensorRT‑LLM deployment pipeline on Triton" translate="no">​</a></h2>
<p>Unlike Ollama, vLLM, or ONNX-based models, where inference engines initialize at runtime, TensorRT-LLM follows an explicit build-then-serve pipeline. The model is compiled into a GPU-specific engine ahead of time, and Triton uses this engine for runtime inference. Once a TensorRT‑LLM engine is built, it can be deployed to any node as long as the hardware and software environment match the conditions it was built for. At a high level, the pipeline consists of three phases:</p>
<table><thead><tr><th>Phase</th><th>Purpose</th><th>Key activities</th></tr></thead><tbody><tr><td><strong>Preparation (Provisioner)</strong></td><td>Transform a standard model into a high‑performance, GPU‑specific executable</td><td>Model weights are converted from Hugging Face format into a TensorRT‑LLM checkpoint, optional quantization is applied to reduce memory footprint, and a GPU‑specific TensorRT engine is compiled with CUDA kernel fusion for maximum throughput.</td></tr><tr><td><strong>Configuration (Template)</strong></td><td>Align the model’s physical characteristics with Triton’s execution requirements</td><td>Template‑based Triton configurations are populated with concrete values such as engine paths, batching limits, and instance counts, ensuring preprocessing, engine, and postprocessing stages agree on shapes and runtime behavior.</td></tr><tr><td><strong>Inference (Runtime)</strong></td><td>Orchestrate live request execution and model serving</td><td>Triton Inference Server loads the prebuilt TensorRT‑LLM engine, orchestrates the ensemble pipeline end‑to‑end, manages concurrency and in‑flight batching, and exposes HTTP and gRPC endpoints for inference.</td></tr></tbody></table>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="for-reference">For reference<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#for-reference" class="hash-link" aria-label="Direct link to For reference" title="Direct link to For reference" translate="no">​</a></h3>
<ul>
<li><a href="https://docs.nvidia.com/tensorrt-llm" target="_blank" rel="noopener noreferrer">TensorRT‑LLM overview</a></li>
<li><a href="https://github.com/NVIDIA/TensorRT-LLM" target="_blank" rel="noopener noreferrer">TensorRT‑LLM engine build workflow</a></li>
<li><a href="https://docs.nvidia.com/deeplearning/triton-inference-server" target="_blank" rel="noopener noreferrer">Triton model repository and ensemble models</a></li>
<li><a href="https://github.com/triton-inference-server/tensorrtllm_backend" target="_blank" rel="noopener noreferrer">TensorRT‑LLM Triton backend</a></li>
<li><a href="https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs" target="_blank" rel="noopener noreferrer">Triton Inference Server architecture</a></li>
<li><a href="https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/user_guide/model_configuration.html" target="_blank" rel="noopener noreferrer">Performance and batching concepts</a></li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="why-this-pipeline-matters">Why this pipeline matters<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#why-this-pipeline-matters" class="hash-link" aria-label="Direct link to Why this pipeline matters" title="Direct link to Why this pipeline matters" translate="no">​</a></h3>
<p>This compile-then-serve model differentiates TensorRT-LLM from earlier approaches in this series. By shifting optimization and hardware specialization to build time, TensorRT-LLM delivers deterministic performance and efficient GPU utilization, which are key requirements for edge and on-prem AI deployments.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="tensorrt-llm-build-and-deployment-directory-structure">TensorRT-LLM build and deployment directory structure<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#tensorrt-llm-build-and-deployment-directory-structure" class="hash-link" aria-label="Direct link to TensorRT-LLM build and deployment directory structure" title="Direct link to TensorRT-LLM build and deployment directory structure" translate="no">​</a></h3>
<p>The following directory structure represents how artifacts flow through the build and deployment phases when using TensorRT-LLM with Triton. This structure helps visualize how model assets, hardware-specific artifacts, and runtime configuration are separated.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/models</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── qwen-raw</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── model.safetensors</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── tokenizer.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── tokenizer_config.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── config.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── model_repository</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── tllm_checkpoint_qwen_7b_int41</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   │   ├── config.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   │   ├── *.ckpt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   │   └── rank0.safetensors</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── tllm_engine_qwen_7b_int41</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       ├── config.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       └── rank0.engine</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── engines</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── qwen_7b_int4</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       ├── config.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       └── rank0.engine</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── tokenizer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── qwen_7b_int4</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       ├── tokenizer.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       └── tokenizer_config.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└── triton_model_repo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── ensemble</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── config.pbtxt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── preprocessing</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── config.pbtxt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── tensorrt_llm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── config.pbtxt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── postprocessing</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── config.pbtxt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    └── tensorrt_llm_bls</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ├── 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        └── config.pbtxt</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="phase-1-preparation">Phase 1: Preparation<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#phase-1-preparation" class="hash-link" aria-label="Direct link to Phase 1: Preparation" title="Direct link to Phase 1: Preparation" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="preparing-storage-for-the-model-repository">Preparing storage for the model repository<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#preparing-storage-for-the-model-repository" class="hash-link" aria-label="Direct link to Preparing storage for the model repository" title="Direct link to Preparing storage for the model repository" translate="no">​</a></h3>
<p>In addition to the prerequisites, you'll need persistent <strong>storage</strong> for model files. First, create a <strong>triton-inference</strong> namespace and a <strong>PersistentVolumeClaim</strong> (PVC) for the model repository.</p>
<p>Save the following YAML as <code>triton-pvc.yaml</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># THE NAMESPACE</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Creates an isolated logical boundary for your Triton resources.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># All subsequent resources must reference this namespace to communicate.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># THE STORAGE (PVC)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Requests a persistent disk from the cluster to store your model weights.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># This ensures that if the Pod restarts, your downloaded models remain intact.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> PersistentVolumeClaim</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">repository</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">pvc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference </span><span class="token comment" style="color:#999988;font-style:italic"># Ensures the storage is available within your namespace</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># ReadWriteOnce (RWO) allows the volume to be mounted as read-write by a single node.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">accessModes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ReadWriteOnce</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Omit storageClassName to use the cluster's default StorageClass.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Allocates 100GB of space. Ensure your underlying disk provider</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># supports this size.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">storage</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 100Gi</span><br></span></code></pre></div></div>
<p>Apply the manifest and verify the PVC status is <strong>Bound</strong> (storage provisioned):</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f triton-pvc.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pvc -n triton-inference</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="deploy-a-helper-pod-to-download-the-model">Deploy a helper pod to download the model<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#deploy-a-helper-pod-to-download-the-model" class="hash-link" aria-label="Direct link to Deploy a helper pod to download the model" title="Direct link to Deploy a helper pod to download the model" translate="no">​</a></h3>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>This tutorial uses a helper pod for downloading models, installing tools, and converting models to engines, making it easier to troubleshoot and iterate. For automated workflows, consider using a Kubernetes <a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/" target="_blank" rel="noopener noreferrer">Job</a> instead, which handles retries and completion tracking natively.</p></div></div>
<p>Next, you'll deploy a temporary helper pod that acts as a provisioner. You'll use this pod to download the model into persistent storage, convert the raw weights into TensorRT-LLM checkpoints, and build a GPU-specific TensorRT engine. This pod <strong>requires a GPU</strong> during the preparation stage. Save the following YAML as <code>triton-provisioner.yaml</code>.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># THE PROVISIONER POD</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># This pod serves as your "workspace" to prepare the environment.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Its purpose is to download raw models, convert them into checkpoints,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># and compile the optimized TensorRT engines for deployment.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Pod</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">provisioner</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> provisioner</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># TRT-LLM Image: Contains the necessary NVIDIA libraries (CUDA, TensorRT-LLM)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># and the Triton server binary required for engine building and testing.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nvcr.io/nvidia/tritonserver</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">25.01</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">trtllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">python</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">py3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Keep the pod alive indefinitely so you can 'kubectl exec' into it</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># and run your download/build commands manually or via scripts.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"/bin/sh"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"-c"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"sleep infinity"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># A GPU is required for the 'trtllm-build' step to compile</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># the engine for the specific GPU architecture in your cluster.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">nvidia.com/gpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">volumeMounts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Mount the PVC to /models inside the container.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># All downloads and engine files saved here will persist across pod restarts.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">storage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">mountPath</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /models</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">storage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Connects the internal /models path to the 100Gi PVC defined earlier.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">persistentVolumeClaim</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">claimName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">repository</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">pvc</span><br></span></code></pre></div></div>
<p>Apply the manifest and wait for the triton-provisioner pod to reach Running (you can stop watching with Ctrl+C once it’s running).</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>EXPECT A WAIT</div><div class="admonitionContent_BuS1"><p>The provisioning pod may take ten minutes or longer to reach the Running state because it pulls a large Triton image that includes CUDA and TensorRT-LLM libraries.</p></div></div>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f triton-provisioner.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n triton-inference -w</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="tensorrt-llm-model-preparation-and-triton-repository-assembly">TensorRT-LLM Model Preparation and Triton Repository Assembly<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#tensorrt-llm-model-preparation-and-triton-repository-assembly" class="hash-link" aria-label="Direct link to TensorRT-LLM Model Preparation and Triton Repository Assembly" title="Direct link to TensorRT-LLM Model Preparation and Triton Repository Assembly" translate="no">​</a></h3>
<p>Save the following script as a <code>prepare-trtllm.sh</code> file. This script prepares a Hugging Face LLM for inference with Triton by downloading the model, converting and quantizing it into a GPU‑specific TensorRT‑LLM engine, assembling the Triton model repository, and generating all required configuration files for end‑to‑end inference.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">#!/usr/bin/env bash</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -euo pipefail</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Print a helpful message if any command fails</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">trap 'echo "FAILED at line $LINENO: $BASH_COMMAND" &gt;&amp;2' ERR</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo "Starting TensorRT-LLM preparation pipeline..."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Paths</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">MODELS_ROOT="/models"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">RAW_MODEL_DIR="${MODELS_ROOT}/qwen-raw"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CHECKPOINT_DIR="${MODELS_ROOT}/model_repository/tllm_checkpoint_qwen_7b_int41"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ENGINE_BUILD_DIR="${MODELS_ROOT}/model_repository/tllm_engine_qwen_7b_int41"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ENGINES_DIR="${MODELS_ROOT}/engines/qwen_7b_int4"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">TOKENIZER_DIR="${MODELS_ROOT}/tokenizer/qwen_7b_int4"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">TRITON_REPO_DIR="${MODELS_ROOT}/triton_model_repo"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">FILL_TEMPLATE_SCRIPT="/app/tools/fill_template.py"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">MODEL_ID="Qwen/Qwen2.5-7B-Instruct"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Small helper to print what is running</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  echo ""</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  echo "RUN: $*"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "$@"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Validate environment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if [[ ! -d "${MODELS_ROOT}" ]]; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  echo "FAILED: ${MODELS_ROOT} does not exist. Ensure the PVC is mounted at /models and run inside the provisioner pod." &gt;&amp;2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  exit 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if ! command -v python3 &gt;/dev/null 2&gt;&amp;1; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  echo "FAILED: python3 not found. Run inside the TRT LLM provisioner image." &gt;&amp;2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  exit 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if ! command -v trtllm-build &gt;/dev/null 2&gt;&amp;1; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  echo "FAILED: trtllm-build not found. Run inside the TRT LLM provisioner image." &gt;&amp;2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  exit 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Enter persistent directory</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run cd "${MODELS_ROOT}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Create required directories (idempotent)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run mkdir -p "${RAW_MODEL_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run mkdir -p "${CHECKPOINT_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run mkdir -p "${ENGINE_BUILD_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo ""</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo "Downloading model ${MODEL_ID} into ${RAW_MODEL_DIR} ..."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Download raw model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Prefer huggingface-cli when available, otherwise fall back to python module invocation</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if command -v huggingface-cli &gt;/dev/null 2&gt;&amp;1; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  run huggingface-cli download "${MODEL_ID}" --local-dir "${RAW_MODEL_DIR}" --exclude "*.bin" "*.pth"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  run python3 -m huggingface_hub.cli download "${MODEL_ID}" --local-dir "${RAW_MODEL_DIR}" --exclude "*.bin" "*.pth"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Convert to quantized checkpoint</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run python3 /app/examples/qwen/convert_checkpoint.py \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --model_dir "${RAW_MODEL_DIR}" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --output_dir "${CHECKPOINT_DIR}" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --dtype float16 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --use_weight_only \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --weight_only_precision int4</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Build the compressed engine</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run trtllm-build \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --checkpoint_dir "${CHECKPOINT_DIR}" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --output_dir "${ENGINE_BUILD_DIR}" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --gemm_plugin float16 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --max_batch_size 4 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --max_input_len 2048 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --max_seq_len 3072</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo ""</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo "Finalizing Triton model repository..."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Create folder layout + copy engine files</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run mkdir -p "${ENGINES_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run cp -f "${ENGINE_BUILD_DIR}/"* "${ENGINES_DIR}/"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Copy Triton model repository templates (overwrite allowed)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run mkdir -p "${TRITON_REPO_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run cp -rf /app/all_models/inflight_batcher_llm/* "${TRITON_REPO_DIR}/"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Verify files copied</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run ls -la "${TRITON_REPO_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Copy tokenizer files</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run mkdir -p "${TOKENIZER_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run cp -f "${RAW_MODEL_DIR}/"tokenizer* "${TOKENIZER_DIR}/"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Fill in model configs with fill_template.py</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if [[ ! -f "${FILL_TEMPLATE_SCRIPT}" ]]; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  echo "FAILED: fill_template.py not found at ${FILL_TEMPLATE_SCRIPT}" &gt;&amp;2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  exit 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export ENGINE_DIR="${ENGINES_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export TOKENIZER_DIR="${TOKENIZER_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export MODEL_FOLDER="${TRITON_REPO_DIR}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export TRITON_MAX_BATCH_SIZE="1"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export INSTANCE_COUNT="1"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export MAX_QUEUE_DELAY_MS="0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export MAX_QUEUE_SIZE="0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export FILL_TEMPLATE_SCRIPT="${FILL_TEMPLATE_SCRIPT}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export DECOUPLED_MODE="false"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export LOGITS_DATATYPE="TYPE_FP32"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Basic validation that expected config files exist before patching</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">for f in \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "${MODEL_FOLDER}/ensemble/config.pbtxt" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "${MODEL_FOLDER}/preprocessing/config.pbtxt" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "${MODEL_FOLDER}/tensorrt_llm/config.pbtxt" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "${MODEL_FOLDER}/postprocessing/config.pbtxt" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "${MODEL_FOLDER}/tensorrt_llm_bls/config.pbtxt"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  if [[ ! -f "$f" ]]; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    echo "FAILED: expected config file missing: $f" &gt;&amp;2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    exit 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  fi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">done</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Update ensemble config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run python3 "${FILL_TEMPLATE_SCRIPT}" -i "${MODEL_FOLDER}/ensemble/config.pbtxt" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "triton_max_batch_size:${TRITON_MAX_BATCH_SIZE},logits_datatype:${LOGITS_DATATYPE}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Update preprocessing config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run python3 "${FILL_TEMPLATE_SCRIPT}" -i "${MODEL_FOLDER}/preprocessing/config.pbtxt" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "tokenizer_dir:${TOKENIZER_DIR},triton_max_batch_size:${TRITON_MAX_BATCH_SIZE},preprocessing_instance_count:${INSTANCE_COUNT}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Update tensorrt_llm config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run python3 "${FILL_TEMPLATE_SCRIPT}" -i "${MODEL_FOLDER}/tensorrt_llm/config.pbtxt" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "triton_backend:tensorrtllm,triton_max_batch_size:${TRITON_MAX_BATCH_SIZE},decoupled_mode:${DECOUPLED_MODE},engine_dir:${ENGINE_DIR},max_queue_delay_microseconds:${MAX_QUEUE_DELAY_MS},batching_strategy:inflight_fused_batching,max_queue_size:${MAX_QUEUE_SIZE},encoder_input_features_data_type:TYPE_FP16,logits_datatype:${LOGITS_DATATYPE}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Update postprocessing config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run python3 "${FILL_TEMPLATE_SCRIPT}" -i "${MODEL_FOLDER}/postprocessing/config.pbtxt" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "tokenizer_dir:${TOKENIZER_DIR},triton_max_batch_size:${TRITON_MAX_BATCH_SIZE},postprocessing_instance_count:${INSTANCE_COUNT}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Update BLS config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run python3 "${FILL_TEMPLATE_SCRIPT}" -i "${MODEL_FOLDER}/tensorrt_llm_bls/config.pbtxt" \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "triton_max_batch_size:${TRITON_MAX_BATCH_SIZE},decoupled_mode:${DECOUPLED_MODE},bls_instance_count:${INSTANCE_COUNT},logits_datatype:${LOGITS_DATATYPE}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo ""</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo "Triton model repository templates have been successfully updated."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo "TensorRT-LLM preparation completed successfully."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">echo "Stop the provisioner pod after this to release the GPU for the Triton server."</span><br></span></code></pre></div></div>
<p>Run the <code>prepare-trtllm.sh</code> script inside provisioner POD using the following sequence of commands:</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>LONG BUILD TIME</div><div class="admonitionContent_BuS1"><p>The engine build step may take 30 minutes or more depending on your GPU. Do not interrupt the process.</p></div></div>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Copy the prepare-trtllm.sh script to the provisioner pod</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl cp ./prepare-trtllm.sh triton-inference/triton-provisioner:/models/prepare-trtllm.sh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Start a bash session inside the pod</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl exec -it triton-provisioner -n triton-inference -- /bin/bash</span><br></span></code></pre></div></div>
<p>Run the following commands inside bash session:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Fix potential Windows line-ending issues (safe no-op on Linux files)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">sed -i 's/\r$//' /models/prepare-trtllm.sh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Make the script executable</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">chmod +x /models/prepare-trtllm.sh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Run the script</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/models/prepare-trtllm.sh</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="deploying-triton-inference-server">Deploying Triton inference server<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#deploying-triton-inference-server" class="hash-link" aria-label="Direct link to Deploying Triton inference server" title="Direct link to Deploying Triton inference server" translate="no">​</a></h3>
<p>With the engine in place, you'll deploy Triton. Save this as <code>triton-deployment.yaml</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># THE SERVICE (The "Phone Number" for your Model)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># This exposes the Triton Inference Server to users outside or inside the cluster.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># LoadBalancer provides a public IP (on cloud providers like AKS/EKS/GKE).</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Change to 'ClusterIP' if you only want internal access.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> LoadBalancer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># HTTP Endpoint: Standard REST queries (standard for most web apps)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">targetPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># gRPC Endpoint: High-performance, low-latency streaming (best for LLMs)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> grpc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8001</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">targetPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8001</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># Metrics Endpoint: For Prometheus/Grafana monitoring (GPU usage, throughput)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> metrics</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8002</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">targetPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8002</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># Connects this Service to any Pod labeled 'app: triton-server'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 5. THE DEPLOYMENT (The Inference Server)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># This is the actual "Running Process" that serves the model to users.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># This can be flipped 1 or 0 to start and stop the Triton server during troubleshooting.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># Must match the version used in your Provisioner to ensure Engine compatibility.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nvcr.io/nvidia/tritonserver</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">25.01</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">trtllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">python</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">py3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">args</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tritonserver"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># Point to the specific folder where your 'ensemble', 'tensorrt_llm', etc., are stored.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--model-repository=/models/triton_model_repo"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># Prevents Triton from guessing configurations; ensures it uses your exact .pbtxt files.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--disable-auto-complete-config"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># Increases timeout for the Python backend (needed for heavy LLM tokenizers/preprocessing).</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--backend-config=python,stub-timeout-seconds=120"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># Verbose logging (Level 3) is great for debugging but very "noisy."</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># In production, change --log-verbose to 0 or 1.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--log-info=true"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--log-warning=true"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--log-error=true"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--log-verbose=3"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8001</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8002</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic"># Triton requires at least one GPU to load the TensorRT-LLM backend.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">nvidia.com/gpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">volumeMounts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># Mount the SAME PVC that the Provisioner used to access the built engine files.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">storage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">mountPath</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /models</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">storage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">persistentVolumeClaim</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">claimName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">repository</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">pvc</span><br></span></code></pre></div></div>
<p>Apply the Triton deployment manifest and verify Triton server is running:</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>GPU RESOURCE CONFLICT</div><div class="admonitionContent_BuS1"><p>The provisioner pod holds the GPU. You must delete it before deploying the Triton server, otherwise the Triton pod will stay in Pending state due to insufficient GPU resources.</p></div></div>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Stop Provisioner POD</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl delete pod triton-provisioner -n triton-inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># wait until provisioner pod is stopped.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n triton-inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Deploy Triton Server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f triton-deployment.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># verify triton server is running.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n triton-inference -w</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Optionally check the Triton Server logs to ensure it starts correctly</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs -f deployment/triton-server -n triton-inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Replace "triton-server-5cb57f9bf-fjk6x" with the pod name from "kubectl get pods -n triton-inference"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs triton-server-5cb57f9bf-fjk6x -n triton-inference --previous</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="validating-the-tensorrt-llm-inference">Validating the TensorRT-LLM inference<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#validating-the-tensorrt-llm-inference" class="hash-link" aria-label="Direct link to Validating the TensorRT-LLM inference" title="Direct link to Validating the TensorRT-LLM inference" translate="no">​</a></h3>
<p>To validate the setup end-to-end, send a request to Triton and verify that TensorRT-LLM returns a valid response.</p>
<ol>
<li>
<p>Expose the Triton service for testing: you'll use port-forwarding. In a separate terminal window (or a new background process), run:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl port-forward -n triton-inference deploy/triton-server 8000:8000</span><br></span></code></pre></div></div>
</li>
<li>
<p>Run an inference request</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Send a query to Triton from your client PowerShell</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ModelName = "ensemble"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$Uri = "http://localhost:8000/v2/models/$ModelName/generate"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$Payload = @{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  text_input = "What is Azure?"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  max_tokens = 64</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  bad_words  = ""</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  stop_words = ""</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$response = Invoke-RestMethod -Uri $Uri -Method Post -Body ($Payload | ConvertTo-Json -Compress) -ContentType "application/json"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$response</span><br></span></code></pre></div></div>
</li>
</ol>
<p>Example output:</p>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">model_name     : ensemble</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">model_version  : 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">sequence_end   : False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">sequence_id    : 0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">sequence_start : False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">text_output    : What is Azure?</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 Azure is Microsoft's cloud computing platform that enables businesses to build, deploy, and manage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 applications and services through a global network of data centers. It offers a wide range of cloud</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 services including Infrastructure as a Service (IaaS), Platform as a Service (PaaS), and Software as a</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 Service (SaaS). Azure</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="cleanup">Cleanup<a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5#cleanup" class="hash-link" aria-label="Direct link to Cleanup" title="Direct link to Cleanup" translate="no">​</a></h3>
<p>To free resources, delete the triton-inference namespace and all its contents:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl delete namespace triton-inference</span><br></span></code></pre></div></div>
<p>If you installed the GPU Operator specifically for this test, you can also uninstall it via Helm to release cluster resources.</p>]]></content:encoded>
            <category>AKS Arc</category>
            <category>AI</category>
            <category>AI Inference</category>
        </item>
        <item>
            <title><![CDATA[Azure Container Storage v2.1.0: Now GA with Elastic SAN]]></title>
            <link>https://blog.aks.azure.com/2026/04/08/acstor-v2.1-ga</link>
            <guid>https://blog.aks.azure.com/2026/04/08/acstor-v2.1-ga</guid>
            <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Explore what’s new in Azure Container Storage v2.1.0 with Elastic SAN integration, modular on-demand installation, and enhanced node selector support.]]></description>
            <content:encoded><![CDATA[<p>Stateful workloads on Kubernetes continue to demand not only faster performance but also larger scale and more streamlined operational simplicity. <a href="https://learn.microsoft.com/azure/storage/container-storage/container-storage-introduction" target="_blank" rel="noopener noreferrer">Azure Container Storage v2.1.0</a> is now generally available with three headline improvements:</p>
<p><strong>Elastic SAN (ESAN) integration</strong>: consolidate hundreds of persistent volumes under a single managed ESAN, bypassing VM disk-attachment limits</p>
<p><strong>Modular on-demand installation</strong>: deploy only the components your chosen storage type requires, reducing install time and cluster footprint</p>
<p><strong>Node selector support</strong>: control where Azure Container Storage components run so you can optimize resource usage across node pools</p>
<p><img decoding="async" loading="lazy" alt="Azure Container Storage v2.1.0 architecture overview" src="https://blog.aks.azure.com/assets/images/acstorv2-architecture-35d291af0227467faa9ccb1411bee1b4.png" width="3060" height="2193" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-elastic-san-for-kubernetes-storage">Why Elastic SAN for Kubernetes storage<a href="https://blog.aks.azure.com/2026/04/08/acstor-v2.1-ga#why-elastic-san-for-kubernetes-storage" class="hash-link" aria-label="Direct link to Why Elastic SAN for Kubernetes storage" title="Direct link to Why Elastic SAN for Kubernetes storage" translate="no">​</a></h2>
<p><a href="https://learn.microsoft.com/azure/storage/elastic-san/" target="_blank" rel="noopener noreferrer">Elastic SAN</a> is a managed, shared block storage service that provides a central pool of capacity and performance including IOPS and throughput. From this pool, you create multiple volumes and attach them to many compute resources.</p>
<p>Below are example ESAN “base capacity” sizes and the ESAN-level provisioned performance you get from that base size (because ESAN scales SAN IOPS + throughput linearly with base TiB). Specifically, each 1 TiB of base capacity adds 5,000 IOPS and 200 MB/s throughput; additional/capacity-only TiB does not increase IOPS or throughput.</p>
<table><thead><tr><th>Base capacity (TiB)</th><th>ESAN provisioned IOPS</th><th>ESAN provisioned throughput</th></tr></thead><tbody><tr><td>1</td><td>5,000</td><td>200 MB/s</td></tr><tr><td>2</td><td>10,000</td><td>400 MB/s</td></tr><tr><td>5</td><td>25,000</td><td>1,000 MB/s</td></tr><tr><td>50</td><td>250,000</td><td>10,000 MB/s</td></tr><tr><td>200</td><td>1,000,000</td><td>40,000 MB/s</td></tr></tbody></table>
<p>Here are the key advantages we see customers looking for:</p>
<p><strong>Scalability beyond VM disk limits</strong>: ESAN uses iSCSI over TCP/IP for volume connectivity, which bypasses traditional VM disk attachment constraints (for example, limits such as 64 disks per VM). On AKS Linux nodes, iSCSI sessions aren't constrained by the same disk-attachment limits, so you can attach significantly more PVs per node. This is a key differentiator for high-volume PV scenarios.</p>
<p><strong>Cost efficiency through storage consolidation</strong>: Elastic SAN provisions capacity at the TiB level, enabling you to consolidate hundreds (or thousands) of smaller GiB-scale PVs under a single SAN. This reduces overprovisioning and lowers management overhead. Use the <a href="https://azure.microsoft.com/pricing/calculator/?msockid=386fb3dcdad3644830f9a576dbe16569" target="_blank" rel="noopener noreferrer">ESAN pricing calculator</a> for a cost estimation of your storage requirements.</p>
<p><strong>Fast attach/detach for burst scale scenarios</strong>: iSCSI session establishment is fast and avoids disk-centric attachment throttling patterns, which matters during burst scale when dozens to hundreds of pods come and go quickly.</p>
<p><strong>Simplified management with an on-ramp to open-source flexibility</strong>: Azure Container Storage v2 is designed so you install only the components needed for the selected storage type. The SAN CSI Driver is also planned to be open sourced to give customers flexibility in how they orchestrate storage.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="which-workloads-benefit-most">Which workloads benefit most<a href="https://blog.aks.azure.com/2026/04/08/acstor-v2.1-ga#which-workloads-benefit-most" class="hash-link" aria-label="Direct link to Which workloads benefit most" title="Direct link to Which workloads benefit most" translate="no">​</a></h2>
<p>Elastic SAN with Azure Container Storage is a strong fit for stateful workloads that combine many volumes with elastic provisioning needs. Here are a few workload patterns we’ve seen align well:</p>
<p><strong>Database as a service (DBaaS) and multi-tenant database platforms</strong>: Many customers run containerized databases such as PostgreSQL and beyond on AKS, either as DBaaS offerings or shared, multi-tenant platforms. In these scenarios, storage choices have a direct impact on scalability, performance, durability, and day‑to‑day operability.
Common requirements include:</p>
<ul>
<li>Scaling database instances efficiently while keeping infrastructure overhead under control</li>
<li>Fast provisioning and attachment of many PVs, especially in multi-tenant or bursty environments</li>
<li>Avoiding monolithic installs when only a single storage backend is needed</li>
</ul>
<p><strong>Large-scale developer environments and per-user volumes</strong>: Another pattern is per-user or per-project environments where each user instance maps to a PV. One example described in planning is a large environment with many pods across multiple clusters, where daily burst creation can stress volume provisioning and attachment if implemented as thousands of independent disks. Elastic SAN enables consolidating volumes under a SAN and accelerating provisioning/attachment flows compared to disk-per-PV approaches.</p>
<p>For using ESAN with Azure Container Storage, see the <a href="https://learn.microsoft.com/azure/storage/container-storage/use-container-storage-with-elastic-san?tabs=cli" target="_blank" rel="noopener noreferrer">configure Elastic SAN documentation</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="on-demand-installation-model">On-demand installation model<a href="https://blog.aks.azure.com/2026/04/08/acstor-v2.1-ga#on-demand-installation-model" class="hash-link" aria-label="Direct link to On-demand installation model" title="Direct link to On-demand installation model" translate="no">​</a></h2>
<p>Azure Container Storage v2.1.0 supports a lightweight, modular installation model. You can enable Azure Container Storage first, then choose a backing storage type when you need it, deploying only the components required for that storage type. You can also add, update, or remove storage components after the initial setup. It supports two installation flows:</p>
<p><strong>Flow A</strong>: Enable Azure Container Storage and choose storage type upfront
If you know which storage type (local NVMe/Elastic SAN) you want to use upfront, enable Azure Container Storage with your preferred storage type so the relevant driver and a default StorageClass can be configured.</p>
<p><strong>Flow B</strong>: Enable Azure Container Storage first, then add storage type later
If you want to keep the initial install lightweight, you can enable Azure Container Storage first and then create a StorageClass of your preferred storage type which triggers installation of the respective CSI driver when you need it.</p>
<p>For detailed steps, see the <a href="https://learn.microsoft.com/azure/storage/container-storage/install-container-storage-aks?pivots=azurecli" target="_blank" rel="noopener noreferrer">on-demand installation documentation</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="node-selector-support-for-component-placement">Node selector support for component placement<a href="https://blog.aks.azure.com/2026/04/08/acstor-v2.1-ga#node-selector-support-for-component-placement" class="hash-link" aria-label="Direct link to Node selector support for component placement" title="Direct link to Node selector support for component placement" translate="no">​</a></h2>
<p>In production clusters, running every component on every node usually isn’t the goal. You might have:</p>
<ul>
<li>a dedicated node pool optimized for storage-heavy workloads</li>
<li>GPU pools where you want to minimize background DaemonSet footprint</li>
<li>mixed compute/storage topologies where placement matters</li>
</ul>
<p>Azure Container Storage v2.1.0 adds node selector support so you can control where Azure Container Storage components run, helping optimize performance and resource usage. The local CSI drivers can be deployed only in selected node pools by configuring node affinity based on the agentpool label. The following example shows a StorageClass configuration:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> storage.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> StorageClass</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> local</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">nvme</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">annotations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">storageoperator.acstor.io/nodeAffinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">      requiredDuringSchedulingIgnoredDuringExecution:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">        nodeSelectorTerms:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">        - matchExpressions:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          - key: kubernetes.azure.com/agentpool</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">            operator: In</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">            values: [mygpu,mygpu2]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">provisioner</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> localdisk.csi.acstor.io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">reclaimPolicy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Delete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">volumeBindingMode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> WaitForFirstConsumer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">allowVolumeExpansion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><br></span></code></pre></div></div>
<p>For more details on configuring component placement, see the <a href="https://learn.microsoft.com/en-us/azure/storage/container-storage/manage-local-container-storage-interface-driver-placement" target="_blank" rel="noopener noreferrer">node selector documentation</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="practical-guidance">Practical guidance<a href="https://blog.aks.azure.com/2026/04/08/acstor-v2.1-ga#practical-guidance" class="hash-link" aria-label="Direct link to Practical guidance" title="Direct link to Practical guidance" translate="no">​</a></h2>
<ul>
<li>
<p>Validate scale targets in your environment: very high PV density scenarios should be tested with your workload’s I/O patterns and cluster topology.</p>
</li>
<li>
<p>Use node selectors to align component placement with your node pool strategy (for example, dedicated storage pools or mixed pools).</p>
</li>
<li>
<p>Consolidate where it makes sense: Elastic SAN is most compelling when you have many PVs and want centralized capacity/performance management.</p>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="getting-started-with-azure-container-storage-v210">Getting started with Azure Container Storage v2.1.0<a href="https://blog.aks.azure.com/2026/04/08/acstor-v2.1-ga#getting-started-with-azure-container-storage-v210" class="hash-link" aria-label="Direct link to Getting started with Azure Container Storage v2.1.0" title="Direct link to Getting started with Azure Container Storage v2.1.0" translate="no">​</a></h2>
<p>Ready to run your stateful workloads using Azure Container Storage v2.1.0? Here are your next steps:</p>
<ul>
<li>
<p>New to Azure Container Storage? Start with our <a href="https://learn.microsoft.com/azure/storage/container-storage/container-storage-introduction" target="_blank" rel="noopener noreferrer">comprehensive documentation</a></p>
</li>
<li>
<p>Want to use Azure Elastic SAN as storage type? Follow <a href="https://learn.microsoft.com/azure/storage/container-storage/use-container-storage-with-elastic-san?tabs=cli" target="_blank" rel="noopener noreferrer">the step-by-step guide</a></p>
</li>
<li>
<p>Want to use local NVMe as storage type? Follow <a href="https://learn.microsoft.com/azure/storage/container-storage/use-container-storage-with-local-disk" target="_blank" rel="noopener noreferrer">the step-by-step guide</a></p>
</li>
<li>
<p>Deploying specific workloads? Check out our updated deployment guide for <a href="https://learn.microsoft.com/azure/aks/postgresql-ha-overview" target="_blank" rel="noopener noreferrer">PostgreSQL</a></p>
</li>
<li>
<p>Want the open-source local CSI driver? Visit our <a href="https://github.com/Azure/local-csi-driver" target="_blank" rel="noopener noreferrer">GitHub repository</a> for installation instructions</p>
</li>
<li>
<p>Have questions or feedback? Reach out to our team at <a href="mailto:AskContainerStorage@microsoft.com" target="_blank" rel="noopener noreferrer">AskContainerStorage@microsoft.com</a></p>
</li>
</ul>]]></content:encoded>
            <category>General</category>
            <category>Developer</category>
            <category>Storage</category>
            <category>Databases</category>
        </item>
        <item>
            <title><![CDATA[Turn your agents into AKS experts: Agent Skills for AKS]]></title>
            <link>https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks</link>
            <guid>https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks</guid>
            <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Announcing agent skills for AKS, easily pluggable into any agent to enhance them with built-in AKS expertise, safer workflows, and reduced developer overhead.]]></description>
            <content:encoded><![CDATA[<p><strong>Agent skills for Azure Kubernetes Service (AKS)</strong> bring production-grade AKS guidance, troubleshooting checklists, and guardrails directly into any compatible AI agent. The first set of skills are now available through the <strong>GitHub Copilot for Azure</strong> extension, with support for VS Code, Visual Studio, Copilot CLI, and Claude.</p>
<p>While AI agents already carry a good baseline of Kubernetes and AKS knowledge, that knowledge is only as current as their training data and varies across models. Skills enhance agents with prescriptive, up-to-date guidance on the tools and processes our AKS engineers use today to make the right AKS decisions across cluster creation, operations, and issue resolution.</p>
<p><img decoding="async" loading="lazy" alt="Diagram showing artistic rendition of the components" src="https://blog.aks.azure.com/assets/images/hero-image-1b853c7a122263d91cc356d67de04b18.png" width="1536" height="1024" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-are-agent-skills">What are agent skills?<a href="https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks#what-are-agent-skills" class="hash-link" aria-label="Direct link to What are agent skills?" title="Direct link to What are agent skills?" translate="no">​</a></h2>
<p><a href="https://learn.microsoft.com/agent-framework/agents/skills" target="_blank" rel="noopener noreferrer">Agent skills</a> are an open standard pioneered by Anthropic for enhancing AI agents with domain-specific expertise in a token-efficient way. You install a skill once, and any compatible agent (such as GitHub Copilot, Claude, Gemini, or others) picks it up automatically for relevant prompts. They provide essential expertise to your agents while staying context efficient, as each skill loads only when your prompt is relevant to the skill's content:</p>
<ul>
<li>If you're not asking about AKS, the skill <strong>stays out of the way</strong> and doesn't add to your token usage.</li>
<li>When you do ask an AKS-related question, the skill <strong>activates automatically</strong> and brings in the right guidance, commands, and context.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="available-skills">Available skills<a href="https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks#available-skills" class="hash-link" aria-label="Direct link to Available skills" title="Direct link to Available skills" translate="no">​</a></h2>
<p>The first release includes the following skills:</p>
<ol>
<li>A high-level <a href="https://github.com/microsoft/GitHub-Copilot-for-Azure/blob/main/plugin/skills/azure-kubernetes/SKILL.md" target="_blank" rel="noopener noreferrer"><strong>AKS best practices</strong> skill</a></li>
<li>Sub-skills for <a href="https://github.com/microsoft/GitHub-Copilot-for-Azure/blob/main/plugin/skills/azure-diagnostics/troubleshooting/aks/aks-troubleshooting.md" target="_blank" rel="noopener noreferrer"><strong>AKS troubleshooting</strong></a></li>
</ol>
<p>The <strong>AKS best practices</strong> skill guides agents through cluster configuration recommendations across networking, upgrade strategy, security, reliability, scale, and more. The guidance reflects what the AKS engineering team recommends for production clusters for optimal performance and uptime, along with specific defaults and critical decisions that apply to AKS. After installing the skill, try the following commands to invoke the skill and receive targeted recommendations for your AKS cluster:</p>
<ul>
<li>"What are the best practice recommendations for a highly reliable and performant AKS cluster?"</li>
<li>"Help me determine the Day-0 decisions I need to make when creating a new AKS cluster."</li>
<li>"What networking setup is best for my AKS cluster?"</li>
</ul>
<p><strong>AKS troubleshooting</strong> covers the most common incident scenarios: node health failures and networking issues. It includes the exact CLI commands and diagnostic sequences that AKS engineers use internally when working on customer incidents. Commands are permission-gated: the skill only suggests and executes commands that your current credentials allow, so there's no risk of unintentional changes. Here's what a typical troubleshooting session looks like in VS Code with the skill active:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">User: What are the best practices for deploying a secure, reliable, and cost-efficient AKS cluster?</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Copilot: I’ll pull AKS-specific best-practice guidance first so I can give you a concise, production-ready checklist for security, reliability, and cost.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Action: Read skill `azure-kubernetes`</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">For AKS, the best results come from making strong Day-0 choices, then layering autoscaling, policy, and operational guardrails.</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-to-get-started">How to get started<a href="https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks#how-to-get-started" class="hash-link" aria-label="Direct link to How to get started" title="Direct link to How to get started" translate="no">​</a></h2>
<p>You have two options: install individual skills directly, or get the full bundle through the GitHub Copilot for Azure plugin.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="option-1-install-through-github-copilot-for-azure-plugin">Option 1: Install through GitHub Copilot for Azure plugin<a href="https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks#option-1-install-through-github-copilot-for-azure-plugin" class="hash-link" aria-label="Direct link to Option 1: Install through GitHub Copilot for Azure plugin" title="Direct link to Option 1: Install through GitHub Copilot for Azure plugin" translate="no">​</a></h3>
<p>The GitHub Copilot for Azure plugin is available through VS Code, Claude, and Copilot CLI and bundles AKS skills alongside 20+ skills across cost optimization, other Azure resources, and deployment workflows. Learn more about <a href="https://learn.microsoft.com/azure/developer/github-copilot-azure/introduction?tabs=vscode" target="_blank" rel="noopener noreferrer">GitHub Copilot for Azure</a>, and get the plugin:</p>
<p><strong>Install the Azure extension for VS Code:</strong></p>
<ol>
<li>Open VS Code and go to the Extensions marketplace (<code>Ctrl+Shift+X</code> / <code>Cmd+Shift+X</code>).</li>
<li>Search for <strong>GitHub Copilot for Azure</strong> and select <strong>Install</strong>. (<a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azure-github-copilot" target="_blank" rel="noopener noreferrer">Direct link to the marketplace</a>)</li>
<li>Open GitHub Copilot Chat in VS Code (the chat icon in the sidebar or <code>Ctrl+Alt+I</code> / <code>Cmd+Alt+I</code>).</li>
<li>Run a prompt that references AKS, such as <em>"What's the recommended upgrade strategy for my AKS cluster?"</em> or <em>"What are the best practices for AKS clusters?"</em>. The AKS skill will install and load automatically.</li>
</ol>
<blockquote>
<p><strong>Note</strong>: If skills don't activate after installing the extension, open the VS Code Command Palette (<code>Ctrl+Shift+P</code> / <code>Cmd+Shift+P</code>) and run <strong>GitHub Copilot for Azure: Refresh Skills</strong>. Alternatively, running any AKS-related prompt will trigger the extension to initialize and load available skills.</p>
</blockquote>
<p><strong>Install the Azure plugin to Claude or Copilot CLI:</strong></p>
<ol>
<li>Add the marketplace with <code>/plugin marketplace add microsoft/azure-skills</code></li>
<li>Install the plugin with <code>/plugin install azure@azure-skills</code></li>
<li>Update the plugin with <code>/plugin update azure@azure-skills</code></li>
<li>Verify that the azure-kubernetes skill has successfully installed with <code>/skills</code></li>
<li>Run a prompt that references AKS, which will invoke the relevant skill.</li>
</ol>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="option-2-install-aks-skills-directly">Option 2: Install AKS skills directly<a href="https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks#option-2-install-aks-skills-directly" class="hash-link" aria-label="Direct link to Option 2: Install AKS skills directly" title="Direct link to Option 2: Install AKS skills directly" translate="no">​</a></h3>
<ol>
<li>To install specific skills directly, use <code>npx skills add https://github.com/microsoft/github-copilot-for-azure --skill [skill name]</code>:<!-- -->
<ol>
<li><code>npx skills add https://github.com/microsoft/github-copilot-for-azure --skill azure-kubernetes</code></li>
<li><code>npx skills add https://github.com/microsoft/github-copilot-for-azure --skill azure-diagnostics</code></li>
</ol>
</li>
<li>Alternatively, download the skill file directly from the repo from the reference links below and move it to your skills directory (e.g. <code>~/.copilot/skills</code> or <code>~/.claude/skills</code>).</li>
<li>Run any AKS-related prompt such as <em>"Review my AKS cluster for best practices"</em>, and the skill will activate automatically.</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="ai-powered-capabilities-for-aks">AI-powered capabilities for AKS<a href="https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks#ai-powered-capabilities-for-aks" class="hash-link" aria-label="Direct link to AI-powered capabilities for AKS" title="Direct link to AI-powered capabilities for AKS" translate="no">​</a></h2>
<p>AKS now has several AI-powered experiences: skills, the <a href="https://blog.aks.azure.com/2025/08/06/aks-mcp-server">AKS MCP server</a>, and the <a href="https://aka.ms/aks/agentic-cli" target="_blank" rel="noopener noreferrer">agentic CLI for AKS</a>. Understanding the differences helps you choose the right combination for your workflow.</p>
<p>The <strong><a href="https://blog.aks.azure.com/2025/08/06/aks-mcp-server">AKS MCP server</a></strong> is a tool layer that pairs directly with skills. Where skills tell the agent <em>what</em> to do, the MCP server gives it the ability to <em>act</em>: securely access your cluster details, run scoped diagnostic commands, and interact with Kubernetes and Azure APIs. Without the AKS MCP server, agents fall back to running direct CLI commands, which lack the structured, permission-aware interface the MCP server provides.</p>
<p>Skills enhance the base knowledge of any agent, including the <strong><a href="https://aka.ms/aks/agentic-cli" target="_blank" rel="noopener noreferrer">agentic CLI for AKS</a></strong> (<code>az aks agent</code>). We're working on built-in support for all AKS skills in the agentic CLI, making it the right choice when you want a purpose-built terminal experience without assembling the individual pieces across tooling and AKS expertise yourself.</p>
<p>The three layers are designed to complement each other:</p>
<table><thead><tr><th style="text-align:left">Capability</th><th style="text-align:left">Role</th><th style="text-align:left">Requires cluster</th><th style="text-align:left">Best for</th></tr></thead><tbody><tr><td style="text-align:left">AKS skills</td><td style="text-align:left">Knowledge</td><td style="text-align:left">No</td><td style="text-align:left">Wide range of scenarios from cluster configuration to troubleshooting/operations</td></tr><tr><td style="text-align:left">AKS MCP server</td><td style="text-align:left">Tools</td><td style="text-align:left">Yes</td><td style="text-align:left">Live diagnostics, cluster state, Azure and Kubernetes API access</td></tr><tr><td style="text-align:left">Agentic CLI for AKS</td><td style="text-align:left">End-to-end experience</td><td style="text-align:left">Yes</td><td style="text-align:left">AI-powered cluster operations and workflows</td></tr></tbody></table>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="creating-your-own-skills">Creating your own skills<a href="https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks#creating-your-own-skills" class="hash-link" aria-label="Direct link to Creating your own skills" title="Direct link to Creating your own skills" translate="no">​</a></h2>
<p>Your team can get the most value from skills by combining AKS-authored skills with skills specific to your organization. If you have existing AKS workflows tailored to your specific workloads, you can encode them into reusable skills using the same format and run them together.</p>
<p>Good candidates for internal skills include:</p>
<ul>
<li>Governance guardrails (required tags, approved regions, allowed SKUs, naming policies)</li>
<li>Security controls (network isolation checks, image and registry policies, workload identity requirements)</li>
<li>Platform standards (ingress patterns, DNS conventions, observability defaults, escalation paths)</li>
<li>Troubleshooting workbooks (common issues that your team faces, specifics on how your clusters are configured, monitoring setup)</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>AKS skills give your agents a baseline of production AKS knowledge using the same guidance, commands, and diagnostic approaches that AKS engineers use. The first release covers best practices and troubleshooting, and we're planning to cover more scenarios based on customer feedback. If you run into issues or have scenarios you'd like to see covered, submit your feedback in the <a href="https://github.com/Azure/AKS/issues/5705" target="_blank" rel="noopener noreferrer">AKS agent skills forum on GitHub</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="resources">Resources<a href="https://blog.aks.azure.com/2026/04/08/agent-skills-for-aks#resources" class="hash-link" aria-label="Direct link to Resources" title="Direct link to Resources" translate="no">​</a></h2>
<ul>
<li>Link to the <a href="https://github.com/microsoft/GitHub-Copilot-for-Azure/blob/main/plugin/skills/azure-kubernetes/SKILL.md" target="_blank" rel="noopener noreferrer">azure-kubernetes skill</a></li>
<li>Link to the <a href="https://github.com/microsoft/GitHub-Copilot-for-Azure/blob/main/plugin/skills/azure-diagnostics/SKILL.md" target="_blank" rel="noopener noreferrer">azure-diagnostics skill</a></li>
<li>Find all skills in the <a href="https://github.com/microsoft/skills/tree/main/.github/plugins/azure-skills" target="_blank" rel="noopener noreferrer">Microsoft/skills repo</a></li>
</ul>]]></content:encoded>
            <category>Agent</category>
            <category>Skills</category>
            <category>MCP</category>
            <category>Developer</category>
        </item>
        <item>
            <title><![CDATA[AI Inference on AKS enabled by Azure Arc: Predictive AI using Triton and ResNet-50]]></title>
            <link>https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4</link>
            <guid>https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4</guid>
            <pubDate>Tue, 07 Apr 2026 00:01:03 GMT</pubDate>
            <description><![CDATA[Run predictive AI at the edge by deploying Triton with the ONNX backend on AKS enabled by Azure Arc for local ResNet 50 GPU inference without cloud dependency.]]></description>
            <content:encoded><![CDATA[<p>In this post, you'll deploy NVIDIA Triton Inference Server on your Azure Kubernetes Service (AKS) enabled by Azure Arc cluster to serve a ResNet-50 image classification model in ONNX format. By the end, you'll have a working predictive AI inference pipeline running on your on-premises GPU hardware.</p>
<p><img decoding="async" loading="lazy" alt="Deploying predictive AI inference with open-source inference servers on AKS enabled by Azure Arc" src="https://blog.aks.azure.com/assets/images/hero-image-4741bd803b20b3d2c686f1d095dc70ce.png" width="750" height="508" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>The previous post covered deploying LLM servers for text generation. This post shifts to predictive AI, specifically computer vision. You'll use NVIDIA Triton Inference Server with the ONNX runtime backend to serve ResNet-50, a classic "hello world" for image classification. Triton is an enterprise-grade, multi-framework model server, and deploying it here gives you a starting pattern for serving any ONNX model on your AKS enabled by Azure Arc managed cluster.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>PREREQUISITES AND SCOPE</div><div class="admonitionContent_BuS1"><p>Before you begin, ensure the <a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2">Part 2 prerequisites</a> are met, including a GPU node configured for <strong>nvidia.com/gpu</strong>. Cluster nodes need <strong>internet access</strong> to download models. <strong>Expect a delay</strong> during initial deployment while the pod downloads and caches model files.</p><p>This tutorial is designed for experimentation and learning. The configurations shown are not production-ready and should not be deployed to production environments without additional security, reliability, performance hardening, and following standard practices.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="ai-inference-with-triton-onnx">AI Inference with Triton (ONNX)<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#ai-inference-with-triton-onnx" class="hash-link" aria-label="Direct link to AI Inference with Triton (ONNX)" title="Direct link to AI Inference with Triton (ONNX)" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="preparing-storage-for-the-model-repository">Preparing storage for the model repository<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#preparing-storage-for-the-model-repository" class="hash-link" aria-label="Direct link to Preparing storage for the model repository" title="Direct link to Preparing storage for the model repository" translate="no">​</a></h3>
<p>In addition to the prerequisites, you'll need persistent <strong>storage</strong> for model files. First, create a <strong>triton-inference</strong> namespace and a <strong>PersistentVolumeClaim</strong> (PVC) for the model repository.</p>
<p>Save the following YAML as <code>triton-pvc.yaml</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 1. THE NAMESPACE</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Creates an isolated logical boundary for your Triton resources.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># All subsequent resources must reference this namespace to communicate.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 2. THE STORAGE (PVC)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Requests a persistent disk from the cluster to store your model weights.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># This ensures that if the Pod restarts, your downloaded models remain intact.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> PersistentVolumeClaim</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">repository</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">pvc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference </span><span class="token comment" style="color:#999988;font-style:italic"># Ensures the storage is available within your namespace</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># ReadWriteOnce (RWO) allows the volume to be mounted as read-write by a single node.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">accessModes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ReadWriteOnce</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Omit storageClassName to use the cluster's default StorageClass. </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Allocates 10GB of space. Ensure your underlying disk provider supports this size.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">storage</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 10Gi</span><br></span></code></pre></div></div>
<p>Apply the manifest and verify the PVC status is <strong>Bound</strong> (storage provisioned):</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f triton-pvc.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pvc -n triton-inference</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="deploy-a-helper-pod-to-download-the-model">Deploy a helper pod to download the model<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#deploy-a-helper-pod-to-download-the-model" class="hash-link" aria-label="Direct link to Deploy a helper pod to download the model" title="Direct link to Deploy a helper pod to download the model" translate="no">​</a></h3>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>This tutorial uses a helper pod for interactive model downloading, which makes it easy to troubleshoot and retry. For automated workflows, consider using a Kubernetes <a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/" target="_blank" rel="noopener noreferrer">Job</a> instead, which handles retries and completion tracking natively.</p></div></div>
<p>Next, you'll spin up a temporary pod to download the model into storage. Save the following YAML as <code>model-download-pod.yaml</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Pod</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Name of the helper pod used to stage or download models</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">downloader</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Namespace where Triton and related components are deployed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># Label to identify this pod for management or cleanup</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">downloader</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">storage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Persistent volume claim backing the Triton model repository</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Models downloaded by this pod will be visible to Triton</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">persistentVolumeClaim</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">claimName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">repository</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">pvc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> helper</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Lightweight Python image used as a utility container</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> python</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">3.11</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">slim</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Keep the container running so you can exec into it</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># and download or manage model files interactively</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"/bin/sh"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"-c"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tail -f /dev/null"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">volumeMounts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">storage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># Mount path where models will be placed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># This should match Triton's model repository path</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">mountPath</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /models</span><br></span></code></pre></div></div>
<p>Apply the manifest and wait for the model-downloader pod to reach Running (you can stop watching with Ctrl+C once it’s running).</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f model-download-pod.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n triton-inference -w</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="download-the-onnx-model-into-the-repository">Download the ONNX model into the repository<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#download-the-onnx-model-into-the-repository" class="hash-link" aria-label="Direct link to Download the ONNX model into the repository" title="Direct link to Download the ONNX model into the repository" translate="no">​</a></h3>
<p>Now <strong>download the ResNet-50 ONNX model</strong> from Hugging&nbsp;Face:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl exec -n triton-inference model-downloader -- python3 -c "</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import urllib.request;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import os;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">os.makedirs('/models/repository/resnet50/1', exist_ok=True);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">urllib.request.urlretrieve(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  'https://huggingface.co/onnxmodelzoo/resnet50-v2-7/resolve/main/resnet50-v2-7.onnx',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  '/models/repository/resnet50/1/model.onnx'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">print('Download Complete')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="confirm-the-model-file-98-mb-exists">Confirm the model file (~98 MB) exists<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#confirm-the-model-file-98-mb-exists" class="hash-link" aria-label="Direct link to Confirm the model file (~98 MB) exists" title="Direct link to Confirm the model file (~98 MB) exists" translate="no">​</a></h4>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl exec -n triton-inference model-downloader -- ls -lh /models/repository/resnet50/1/model.onnx</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="deploying-triton-inference-server-onnx">Deploying Triton inference server (ONNX)<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#deploying-triton-inference-server-onnx" class="hash-link" aria-label="Direct link to Deploying Triton inference server (ONNX)" title="Direct link to Deploying Triton inference server (ONNX)" translate="no">​</a></h3>
<p>With the model in place, you'll deploy Triton. Save this as <code>triton-deployment.yaml</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Name of the Triton Inference Server deployment</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Namespace where Triton and related resources are deployed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Number of Triton pods to run</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Label selector used by the Deployment to manage pods</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Labels applied to the Triton pod</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Official NVIDIA Triton Inference Server image</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nvcr.io/nvidia/tritonserver</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">24.08</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">py3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Start Triton with the model repository mounted at /models/repository</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># --strict-model-config=false allows Triton to infer model configs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">args</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"tritonserver"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--model-repository=/models/repository"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--strict-model-config=false"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># HTTP endpoint for inference and health checks</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># gRPC endpoint for inference</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8001</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> grpc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># READINESS PROBE:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Checks whether Triton is ready to accept inference requests.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Triton exposes /v2/health/ready and returns HTTP 200 only after</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># the server is initialized and all models are loaded.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">readinessProbe</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">httpGet</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /v2/health/ready</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">initialDelaySeconds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Wait 60 seconds before the first probe</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">periodSeconds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># Probe every 10 seconds</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">failureThreshold</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># Mark pod unready after 6 failures</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># Request exclusive access to one NVIDIA GPU</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">nvidia.com/gpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">volumeMounts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">vol</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># Mount the persistent volume containing the Triton model repository</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">mountPath</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /models</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">vol</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Persistent volume claim backing the Triton model repository</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">persistentVolumeClaim</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">claimName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triton</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">repository</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">pvc</span><br></span></code></pre></div></div>
<p>Apply the Triton deployment manifest and verify Triton server is running</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f triton-deployment.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n triton-inference -w   # watch Triton pod startup</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="validating-the-resnet-model-inference">Validating the ResNet model inference<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#validating-the-resnet-model-inference" class="hash-link" aria-label="Direct link to Validating the ResNet model inference" title="Direct link to Validating the ResNet model inference" translate="no">​</a></h3>
<p>Now you'll test it end-to-end. You'll send an image to Triton and confirm you get a valid prediction back from ResNet-50.</p>
<ol>
<li>
<p>Expose the Triton service for testing: Since there's no LoadBalancer service, you'll use port-forwarding. In a separate terminal window (or a new background process), run:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl port-forward -n triton-inference deploy/triton-server 8000:8000</span><br></span></code></pre></div></div>
</li>
<li>
<p>Prepare a test image: Obtain a sample image file (e.g., a JPEG picture of an animal, since ResNet-50 is a general image classifier). Save the image to a known path on your local machine (for example, <code>C:\temp\test-image.jpg</code>).</p>
</li>
<li>
<p>Install the Python dependencies the script needs (if you haven't already):</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip install numpy pillow requests</span><br></span></code></pre></div></div>
</li>
<li>
<p>Run an inference request using a script. Save the following script as <code>ImageClassificationSample.ps1</code>, then execute it to submit a request to Triton:</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>ResNet‑50 predicts one of 1,000 ImageNet classes. This sample applies a demo‑only mapping that groups some animal‑related labels for simplicity. You can extend the sample to handle additional classes or custom mappings. This is provided for demonstration purposes only.</p></div></div>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># ImageClassificationSample.ps1 - sends an image to Triton Inference Server and prints the result.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 1. Prompt for the local image path to classify:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$imagePath = Read-Host "Enter the full path to your local image (e.g., C:\pics\animal.jpg)"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$tritonUrl = "http://localhost:8000/v2/models/resnet50/infer"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 2. Define a Python script that will send the image to Triton:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$scriptPath = "$env:TEMP\triton_silent_exec.py"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$scriptContent = @'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import numpy as np</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from PIL import Image</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import requests</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import sys</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Determine broad animal type from predicted label (for demonstration)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">def get_animal_type(label):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    l = label.lower()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    mapping = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "DOG": ['dog', 'terrier', 'retriever', 'hound', 'collie', 'beagle', 'malamute', 'husky', 'dalmatian', 'pug'],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "CAT": ['cat', 'tabby', 'siamese', 'persian', 'lynx', 'leopard', 'tiger', 'lion', 'cougar', 'cheetah'],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "HORSE": ['horse', 'stallion', 'foal', 'zebra', 'sorrel'],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "DONKEY": ['donkey', 'mule'],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "BUFFALO": ['buffalo', 'bison', 'water buffalo'],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "DEER": ['deer', 'impala', 'gazelle', 'elk', 'moose', 'antelope']</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    for category, keywords in mapping.items():</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        if any(x in l for x in keywords):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            return category</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    return "OTHER"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">def main():</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    try:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        img_path, url = sys.argv[1], sys.argv[2]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        # Fetch labels for ImageNet classes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        labels_resp = requests.get("https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt", timeout=10)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        labels = [line.strip() for line in labels_resp.text.strip().splitlines() if line.strip()]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        # Load and preprocess the image</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        img = Image.open(img_path).convert('RGB').resize((224, 224))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        img_data = (np.array(img).astype(np.float32) / 255.0).transpose(2, 0, 1)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        img_data = np.expand_dims(img_data, axis=0)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        # Prepare binary data and header for Triton HTTP inference request</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        binary_data = img_data.tobytes()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        header = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "inputs": [{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                "name": "data",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                "shape": [1, 3, 224, 224],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                "datatype": "FP32",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                "parameters": {"binary_data_size": len(binary_data)}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        header_str = json.dumps(header)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        body = header_str.encode('utf-8') + binary_data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        headers = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "Inference-Header-Content-Length": str(len(header_str)),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "Content-Type": "application/octet-stream"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        # Send the request to Triton's HTTP inference endpoint</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        response = requests.post(url, data=body, headers=headers, timeout=15)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        res = response.json()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        if "error" in res:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            print(f"Error: {res['error']}")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        else:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            scores = res["outputs"][0]["data"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            idx = scores.index(max(scores))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            breed = labels[idx]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            # Print results</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            print("\n--- AI INFERENCE RESULT ---")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            print(f"ANIMAL TYPE    : {get_animal_type(breed)}")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            print(f"SPECIFIC BREED : {breed}")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            print(f"SCORE     : {max(scores):.4f}")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    except Exception as e:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        print(f"Execution Failed: {e}")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if __name__ == '__main__':</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    main()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">'@</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 3. Execute the Python inference script with the provided image:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Write-Host "Processing inference..." -ForegroundColor Cyan</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$scriptContent | Out-File -FilePath $scriptPath -Encoding utf8 -Force</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">python $scriptPath "$imagePath" "$tritonUrl"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 4. Clean up the temporary script file:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if (Test-Path $scriptPath) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Remove-Item $scriptPath</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
</li>
</ol>
<p>Example output:</p>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">PS D:\Dynamo-Triton&gt; powershell.exe -ExecutionPolicy Bypass -File .\ImageClassificationSample.ps1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Enter the full path to your local image (e.g., C:\pics\animal.jpg): "D:\Dynamo-Triton\susanneedele-quarter-horse.jpg"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Processing Inference...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--- AI INFERENCE RESULT ---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ANIMAL TYPE    : HORSE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SPECIFIC BREED : sorrel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SCORE          : 12.5520</span><br></span></code></pre></div></div>
<p>The script maps ImageNet labels to broad animal categories for readability; your actual output will include the full class name.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="cleanup">Cleanup<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#cleanup" class="hash-link" aria-label="Direct link to Cleanup" title="Direct link to Cleanup" translate="no">​</a></h3>
<p>To free resources, delete the triton-inference namespace and all its contents:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl delete namespace triton-inference</span><br></span></code></pre></div></div>
<p>If you installed the GPU Operator specifically for this test, you can also uninstall it via Helm to release cluster resources.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="troubleshooting">Troubleshooting<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4#troubleshooting" class="hash-link" aria-label="Direct link to Troubleshooting" title="Direct link to Troubleshooting" translate="no">​</a></h3>
<p>Here are common issues and how to fix them:</p>
<ul>
<li>Triton pod stuck in Pending state: This typically means no suitable node was found with the required GPU resource. Ensure that your cluster has a GPU node available and that the NVIDIA device plugin is running on it. If you haven’t installed the GPU operator or device plugin, revisit the Prepare GPU nodes step above or see the <a href="https://learn.microsoft.com/azure/aks/aksarc/use-gpu-workloads" target="_blank" rel="noopener noreferrer">GPU workloads on AKS Arc</a> documentation.</li>
<li>Model fails to download: If the model-downloader pod cannot retrieve the model from the internet (for example, kubectl exec times out or the model file is not present), check that the cluster’s network can access external endpoints such as Hugging Face. If necessary, manually download the model file to your local machine and then transfer it to the cluster (for instance, by attaching a file to the PVC or using a different container image with the model baked in).</li>
<li>Triton server not loading the model: If the Triton pod is running but the model isn’t being served (e.g., inference requests return errors), verify the Triton container logs for any errors. Use kubectl logs -n triton-inference deploy/triton-server to see Triton’s output. Check for messages about loading the model; if there are errors, ensure the model repository path is correct (it should be /models/repository inside the container) and that the model file is correctly placed under the folder structure resnet50/1/model.onnx. You can also exec into the Triton pod to confirm the model file exists in the expected location.</li>
<li>Inference request errors: If the PowerShell test script returns a JSON parsing error or other failure, ensure you are using PowerShell 7+ (as older Windows PowerShell versions may not handle the JSON body formatting correctly). Additionally, check that the port-forward is active and that you are using the correct URL (<code>http://localhost:8000/v2/models/resnet50/infer</code>). If using a different model or if you modified the deployment, adjust the model name and endpoint accordingly.</li>
</ul>
<p>You now have Triton running on your Arc-enabled AKS cluster, serving predictions from a ResNet-50 model. From here, you can swap in your own ONNX models or try different Triton backends for other AI workloads.</p>]]></content:encoded>
            <category>AKS Arc</category>
            <category>AI</category>
            <category>AI Inference</category>
        </item>
        <item>
            <title><![CDATA[AI Inference on AKS enabled by Azure Arc: Generative AI with Open‑Source LLM Server]]></title>
            <link>https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3</link>
            <guid>https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3</guid>
            <pubDate>Tue, 07 Apr 2026 00:01:02 GMT</pubDate>
            <description><![CDATA[Run generative AI at the edge with Ollama and vLLM on AKS enabled by Azure Arc, enabling local LLM inference on GPU hardware without dependency or data egress.]]></description>
            <content:encoded><![CDATA[<p>In this post, you'll explore how to deploy and run generative AI inference workloads using open-source large language model servers on Azure Kubernetes Service (AKS) enabled by Azure Arc. You'll focus on running these workloads locally, on-premises or at the edge, using GPU acceleration with centralized management.</p>
<p><img decoding="async" loading="lazy" alt="Deploying generative AI inference with open-source LLM servers on AKS Arc" src="https://blog.aks.azure.com/assets/images/hero-image-1636a3a7174bb4aa8821568c402d31cc.png" width="735" height="527" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>Rather than using managed AI services, you'll deploy Ollama and vLLM as standalone Kubernetes workloads directly on your cluster. This keeps things transparent. You'll see exactly how model serving, GPU scheduling, and inference requests work inside your Arc-managed environment. Performance tuning and benchmarks are out of scope here; the focus is a clear, repeatable, and diagnosable starting point for GPU-accelerated inference. These fundamentals set you up for the more advanced architectures covered in later posts.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>PREREQUISITES AND SCOPE</div><div class="admonitionContent_BuS1"><p>Before you begin, ensure the <a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2">Part 2 prerequisites</a> are met, including a GPU node configured for <strong>nvidia.com/gpu</strong>. Cluster nodes need <strong>internet access</strong> to download models. <strong>Expect a delay</strong> during initial deployment while the pod downloads and caches model files.</p><p>This tutorial is designed for experimentation and learning. The configurations shown are not production-ready and should not be deployed to production environments without additional security, reliability, performance hardening, and following standard practices.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="ai-inference-with-ollama">AI inference with Ollama<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#ai-inference-with-ollama" class="hash-link" aria-label="Direct link to AI inference with Ollama" title="Direct link to AI inference with Ollama" translate="no">​</a></h2>
<p>Now that your environment is ready, you'll deploy the Ollama model server. You'll use Ollama's official container image to serve a large language model, specifically <strong>Phi-3 Mini</strong> with 4-bit quantization (~2.2 GB), which fits comfortably on a single <strong>16 GB GPU</strong>. Once deployed, you'll have a single endpoint that supports both Ollama's native REST API and an OpenAI-compatible interface.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="deploying-the-ollama-model-server">Deploying the Ollama model server<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#deploying-the-ollama-model-server" class="hash-link" aria-label="Direct link to Deploying the Ollama model server" title="Direct link to Deploying the Ollama model server" translate="no">​</a></h3>
<p>If your cluster has multiple GPU nodes, apply the <code>accelerator=nvidia-gpu</code> label to a node to ensure the Ollama pod schedules on your target hardware.</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 1. FIND THE GPU NODE NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># This script performs a deep search of your cluster's hardware resources.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get nodes -o json | ConvertFrom-Json | ForEach-Object { $_.items } | Where-Object { $_.status.allocatable.'nvidia.com/gpu' -gt 0 } | Select-Object -ExpandProperty metadata | Select-Object -ExpandProperty name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 2. APPLY THE APPLICATION LABEL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># This identifies the node as the designated "home" for the Ollama application.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Useful for organizational filtering: 'kubectl get nodes -l app=ollama'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl label node &lt;node name&gt;  app=ollama</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 3. APPLY THE HARDWARE LABEL FOR SCHEDULING AFFINITY</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># This matches the 'nodeSelector' in the Deployment YAML below.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># This creates a "Hardware Requirement" tag on the Node's metadata.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl label node &lt;node name&gt; accelerator=nvidia-gpu</span><br></span></code></pre></div></div>
<p>Next, create a Kubernetes manifest (e.g. ollama-deployment.yaml) for the Ollama Deployment and Service:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 1. NAMESPACE: Creates a dedicated logical "room" for your Ollama resources.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># This prevents your models and services from cluttering the 'default' namespace.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 2. DEPLOYMENT: This manages the lifecycle of your Ollama container.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># It ensures that if the pod crashes, a new one is automatically started.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># SELECTOR: The Deployment controller uses this to find the Pods it owns.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># POD LABELS: These are the "tags" applied to the actual running container.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># These MUST match the selector above and the Service selector below.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># NODE SELECTOR: This is the hardware "constraint."</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># It forces the pod to land ONLY on a node you have labeled 'accelerator: nvidia-gpu'.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">nodeSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">accelerator</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nvidia</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gpu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama/ollama</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">0.18.3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># PORT NAME</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Note: Kubernetes still uses TCP under the hood (the default protocol).</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">11434</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># READINESS PROBE: Tells Kubernetes when this pod is ready to accept traffic.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Ollama responds with "Ollama is running" on GET / when the server is up.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># The Service will not route requests here until this probe succeeds.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">readinessProbe</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">httpGet</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">11434</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">initialDelaySeconds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># Wait 10s after container start before first check.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">periodSeconds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># Check every 5 seconds after that.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">failureThreshold</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># Mark unready after 3 consecutive failures.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># RESOURCE LIMITS: This is the actual "handshake" with the NVIDIA driver.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># It tells the cluster to carve out 1 physical GPU for this pod.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">nvidia.com/gpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 3. SERVICE: This acts as the "Front Door" or Load Balancer for the Pod.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># It provides a stable IP address so you can talk to the Ollama API.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># TYPE: LoadBalancer requests a public/external IP from your cloud provider.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Use 'ClusterIP' instead if you only want to access this from inside the cluster.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> LoadBalancer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># SERVICE SELECTOR: This tells the Service, "Find any pod with the label 'app: ollama'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># in this namespace and send traffic to it."</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">11434</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># The port you hit on the LoadBalancer IP.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">targetPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">11434</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># The port the Ollama application is listening on inside the pod.</span><br></span></code></pre></div></div>
<p>Apply the manifest to start the Ollama server. On Azure Local, if no external load balancer is available, use port-forwarding to access the service.</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f ollama-deployment.yaml                  # apply deployment yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -l app=ollama -n ollama-inference -w    # watch pod status</span><br></span></code></pre></div></div>
<p>Wait until the ollama pod is Running!</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="loading-a-model-and-testing-inference">Loading a model and testing inference<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#loading-a-model-and-testing-inference" class="hash-link" aria-label="Direct link to Loading a model and testing inference" title="Direct link to Loading a model and testing inference" translate="no">​</a></h3>
<p>Once the server is running, load a test model and send an inference API request. The example below uses a small (~2.2&nbsp;GB) model called “phi3”. Run the following to pull the model weights inside the running Ollama pod:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">$podName = kubectl get pods -n ollama-inference -l app=ollama -o jsonpath='{.items[0].metadata.name}'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl exec -it $podName -n ollama-inference -- ollama pull phi3</span><br></span></code></pre></div></div>
<p>After the ollama pull command prints “success,” the model is ready. Now issue a test generate request to the server’s HTTP API (port 11434). For example, using PowerShell:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Setup Port Forwarding if your client machine and AKS enabled by Azure Arc clusters are not on the same network</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl port-forward svc/ollama-service -n ollama-inference 11434</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Use localhost with port-forward (if using external IP, replace URI accordingly):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># To use the OpenAI-compatible interface, switch the URI to "http://localhost:11434/v1/chat/completions"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Invoke-RestMethod -Method Post -Uri "http://localhost:11434/api/generate" `</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    -ContentType "application/json" `</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    -Body '{"model": "phi3", "prompt": "What is Azure Kubernetes Service (AKS) enabled by Azure Arc?", "stream": false}'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Example output:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">model                : phi3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">created_at           : 2026-03-26T16:41:37.52900295Z</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">response             : Azure Kubernetes Service (AKS) leverages the capabilities of Azure Arc to extend its infrastructure </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                       management across multiple environments, including on-premises and other cloud services. .......</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">done                 : True</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">done_reason          : stop</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">context              : {32010, 29871, 13, 5618...}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">total_duration       : 6388662955</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">load_duration        : 181600130</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">prompt_eval_count    : 21</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">prompt_eval_duration : 34590990</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">eval_count           : 328</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">eval_duration        : 5894037253</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="cleanup-ollama">Cleanup Ollama<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#cleanup-ollama" class="hash-link" aria-label="Direct link to Cleanup Ollama" title="Direct link to Cleanup Ollama" translate="no">​</a></h3>
<p>When finished, remove the Ollama resources to free up the GPU.</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># REMOVE ALL RESOURCES:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Deletes the 'ollama-inference' namespace and everything inside it.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># This includes the Deployment (Ollama pod), the Service (LoadBalancer/IP),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># and any local configurations. This is the "factory reset" for this app.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl delete namespace ollama-inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># remove node labels if added</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$nodeName = (kubectl get nodes -l app=ollama -o jsonpath='{.items[0].metadata.name}')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl label node $nodeName app-</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$nodeName = (kubectl get nodes -l accelerator=nvidia-gpu -o jsonpath='{.items[0].metadata.name}')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl label node $nodeName accelerator-</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="ai-inference-with-vllm">AI inference with vLLM<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#ai-inference-with-vllm" class="hash-link" aria-label="Direct link to AI inference with vLLM" title="Direct link to AI inference with vLLM" translate="no">​</a></h2>
<p>Make sure you have completed the Ollama cleanup above. In this step, you will deploy a <strong>Mistral 7B</strong> model (AWQ quantized, ~4 GB) using vLLM's OpenAI‑compatible API, then submit a prompt to validate the response.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="deploying-the-vllm-model-server">Deploying the vLLM model server<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#deploying-the-vllm-model-server" class="hash-link" aria-label="Direct link to Deploying the vLLM model server" title="Direct link to Deploying the vLLM model server" translate="no">​</a></h3>
<p>Label the GPU node for scheduling affinity, the same as in the Ollama section above.</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 1. FIND THE GPU NODE NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># This script performs a deep search of your cluster's hardware resources.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get nodes -o json | ConvertFrom-Json | ForEach-Object { $_.items } | Where-Object { $_.status.allocatable.'nvidia.com/gpu' -gt 0 } | Select-Object -ExpandProperty metadata | Select-Object -ExpandProperty name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 2. APPLY THE APPLICATION LABEL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl label node &lt;node name&gt;  app=vllm-mistral</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 3. APPLY THE HARDWARE LABEL FOR SCHEDULING AFFINITY</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl label node &lt;node name&gt; accelerator=nvidia-gpu</span><br></span></code></pre></div></div>
<p>Next prepare a Kubernetes manifest (e.g. vllm-deployment.yaml) to run the vLLM server and expose it:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 1. NAMESPACE: Creates a dedicated "room" for vLLM resources.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 2. DEPLOYMENT: Manages the vLLM inference engine pod.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mistral</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference  </span><span class="token comment" style="color:#999988;font-style:italic"># Scopes this deployment to the vllm-inference namespace</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># SELECTOR: Connects the Deployment controller to the specific Pods it manages.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mistral</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># POD LABELS: Provides identity for the Pod.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># The Service uses 'app: vllm-mistral' to route incoming API requests here.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mistral</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># NODE SELECTOR: Hardware targeting.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Forces the Pod to land on a node you have labeled 'accelerator: nvidia-gpu'.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">nodeSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">accelerator</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nvidia</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gpu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">container</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm/vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">openai</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">v0.18.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"python3"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"-m"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vllm.entrypoints.openai.api_server"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Note: Public models (e.g., TheBloke/Mistral-7B-v0.1-AWQ) work without auth; gated models </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># (e.g., Llama 3) require HUGGING_FACE_HUB_TOKEN to be set in the container environment.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">args</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"--model"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"TheBloke/Mistral-7B-v0.1-AWQ"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token string" style="color:#e3116c">"--quantization"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"awq"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--dtype"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"float16"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token string" style="color:#e3116c">"--host"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0.0.0.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--port"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"8000"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token string" style="color:#e3116c">"--max-model-len"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"4096"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--gpu-memory-utilization"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0.80"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token string" style="color:#e3116c">"--enforce-eager"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># READINESS PROBE: Tells Kubernetes when this pod is ready to serve inference.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># vLLM exposes a /health endpoint that returns HTTP 200 once the model is loaded.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Until this probe passes, the Service will not send any traffic to the pod.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">readinessProbe</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">httpGet</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /health</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">initialDelaySeconds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">120</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># vLLM needs time to download and load the model.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">periodSeconds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token plain">         </span><span class="token comment" style="color:#999988;font-style:italic"># Check every 10 seconds after the initial delay.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">failureThreshold</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># Allow up to 60s of failures before marking unready.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># RESOURCE LIMITS: Ensures 1 physical GPU is reserved for this pod.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">nvidia.com/gpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">volumeMounts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> shm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">mountPath</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /dev/shm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> shm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># SHM (Shared Memory): Required by vLLM/PyTorch for fast data exchange</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># between GPU and CPU. 'Memory' medium uses RAM instead of disk.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">emptyDir</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">medium</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Memory</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">sizeLimit</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"2Gi"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 3. SERVICE: Provides a stable entry point for the vLLM API.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># TYPE: LoadBalancer requests an external IP from your provider.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> LoadBalancer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># SERVICE SELECTOR: Routes traffic to any pod carrying the 'app: vllm-mistral' label.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vllm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mistral</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">protocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> TCP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain">           </span><span class="token comment" style="color:#999988;font-style:italic"># The port you access externally (e.g., http://EXTERNAL_IP:80)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">targetPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8000</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># The port the vLLM container is actually listening on</span><br></span></code></pre></div></div>
<p>Apply the manifest to start the vLLM server:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f vllm-deployment.yaml                       # apply deployment yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -l app=vllm-mistral -n vllm-inference -w   # wait for vllm-mistral pod to run</span><br></span></code></pre></div></div>
<p>Wait for the vllm-mistral pod to reach Running. If no external IP is assigned, port-forward with <code>kubectl port-forward svc/vllm-service -n vllm-inference 8080:80</code> to access the API at <code>http://localhost:8080</code>.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="testing-the-llm-endpoint">Testing the LLM endpoint<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#testing-the-llm-endpoint" class="hash-link" aria-label="Direct link to Testing the LLM endpoint" title="Direct link to Testing the LLM endpoint" translate="no">​</a></h3>
<p>With the vLLM server ready, send a test completion request to verify the deployed model. Using PowerShell’s Invoke-RestMethod, call the /v1/completions endpoint with a JSON body specifying the model and a prompt:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Using localhost with port-forward; replace $SERVICE_IP if using external LB</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Invoke-RestMethod -Method Post -Uri "http://localhost:8080/v1/completions" `</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      -ContentType "application/json" `</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      -Body '{"model": "TheBloke/Mistral-7B-v0.1-AWQ", "prompt": "What is Azure Kubernetes Service (AKS) enabled by Azure Arc", "max_tokens": 100}' |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Select-Object -ExpandProperty choices | Select-Object -ExpandProperty text</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Example output:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Azure Kubernetes Service (AKS) Arc is a managed service provided by Microsoft that allows you to manage your Kubernetes deployment and monitor metrics across multiple clusters using Azure Portal.</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="cleanup-vllm">Cleanup vLLM<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#cleanup-vllm" class="hash-link" aria-label="Direct link to Cleanup vLLM" title="Direct link to Cleanup vLLM" translate="no">​</a></h3>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># When finished, remove the vllm resources to free up the GPU.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl delete namespace vllm-inference</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Remove node labels if added</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$nodeName = (kubectl get nodes -l app=vllm-mistral -o jsonpath='{.items[0].metadata.name}')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl label node $nodeName app-</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$nodeName = (kubectl get nodes -l accelerator=nvidia-gpu -o jsonpath='{.items[0].metadata.name}')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl label node $nodeName accelerator-</span><br></span></code></pre></div></div>
<p>If no more GPU inference is needed, you can also remove the GPU Operator (<code>helm uninstall &lt;release-name&gt;</code>) to reclaim cluster resources.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="next-up-predictive-ai-using-triton-and-resnet-50">Next up: <a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4">Predictive AI using Triton and ResNet-50</a><a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3#next-up-predictive-ai-using-triton-and-resnet-50" class="hash-link" aria-label="Direct link to next-up-predictive-ai-using-triton-and-resnet-50" title="Direct link to next-up-predictive-ai-using-triton-and-resnet-50" translate="no">​</a></h3>]]></content:encoded>
            <category>AKS Arc</category>
            <category>AI</category>
            <category>AI Inference</category>
        </item>
        <item>
            <title><![CDATA[AI Inference on AKS enabled by Azure Arc: Series Introduction and Scope]]></title>
            <link>https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2</link>
            <guid>https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2</guid>
            <pubDate>Tue, 07 Apr 2026 00:01:01 GMT</pubDate>
            <description><![CDATA[Scenario driven series for generative and predictive AI inference on AKS enabled by Azure Arc, across CPUs, GPUs, and NPUs in on-premises and edge environments.]]></description>
            <content:encoded><![CDATA[<p>This series gives you <strong>practical, step-by-step guidance</strong> for experimentation with generative and predictive AI inference workloads on Azure Kubernetes Service (AKS) enabled by Azure Arc clusters, using CPUs, GPUs, and neural processing units (NPUs). The scenarios target on‑premises and edge environments, specifically Azure Local, with a focus on <strong>repeatable, hands-on experimentation</strong> rather than abstract examples.</p>
<p><img decoding="async" loading="lazy" alt="AI inference on AKS enabled by Azure Arc series overview showing generative and predictive AI patterns across hybrid environments" src="https://blog.aks.azure.com/assets/images/hero-image-0f608d7fe306c04d15ab30b655f2397e.png" width="865" height="474" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p><a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-1">Part 1</a> covered why running AI inference at the edge matters. This post defines the series scope, ground rules, and shared prerequisites so each tutorial can focus on the hands-on walkthrough.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="scope-and-expectations">Scope and expectations<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2#scope-and-expectations" class="hash-link" aria-label="Direct link to Scope and expectations" title="Direct link to Scope and expectations" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_BuS1"><p>These tutorials are designed for experimentation and learning. The configurations shown are not production-ready and should not be deployed to production environments without additional security, reliability, performance hardening, and following standard practices.</p></div></div>
<p>This series assumes familiarity with Kubernetes fundamentals, proficiency with kubectl, Azure CLI, and Helm, and experience using AKS enabled by Azure Arc on Azure Local. The focus is not on model development or training.</p>
<p>All scenarios use the same AKS enabled by Azure Arc environment and follow a consistent structure. Inference execution always occurs locally on the cluster. No managed Azure AI services are used. Each scenario follows the same steps: <strong>connect and verify</strong> cluster access, <strong>prepare the accelerator</strong> if required, <strong>deploy the inference workload</strong>, <strong>validate inference</strong> with a test request, and <strong>clean up resources</strong>.</p>
<p>For reference:</p>
<ul>
<li><a href="https://learn.microsoft.com/training/modules/intro-to-kubernetes/" target="_blank" rel="noopener noreferrer">Kubernetes fundamentals</a></li>
<li><a href="https://learn.microsoft.com/azure/azure-arc/kubernetes/overview" target="_blank" rel="noopener noreferrer">AKS enabled by Azure Arc overview</a></li>
<li><a href="https://learn.microsoft.com/azure/aks/aksarc/" target="_blank" rel="noopener noreferrer">AKS enabled by Azure Arc documentation</a></li>
<li><a href="https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/getting-started.html" target="_blank" rel="noopener noreferrer">GPU Operator internals</a></li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="series-outline">Series outline<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2#series-outline" class="hash-link" aria-label="Direct link to Series outline" title="Direct link to Series outline" translate="no">​</a></h2>
<p>The series is designed to evolve. New topics will be added as additional scenarios, runtimes, and architectures are explored.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="topics-covered-in-this-series">Topics covered in this series<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2#topics-covered-in-this-series" class="hash-link" aria-label="Direct link to Topics covered in this series" title="Direct link to Topics covered in this series" translate="no">​</a></h3>
<table><thead><tr><th>Topic</th><th>Type</th><th>Status</th></tr></thead><tbody><tr><td><a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3"><strong>Ollama</strong>: open-source LLM server</a></td><td>Generative</td><td>✅ Available</td></tr><tr><td><a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3"><strong>vLLM</strong>: high-throughput LLM engine</a></td><td>Generative</td><td>✅ Available</td></tr><tr><td><a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-4"><strong>Triton + ONNX</strong>: ResNet‑50 image classification</a></td><td>Predictive</td><td>✅ Available</td></tr><tr><td><a href="https://blog.aks.azure.com/2026/04/09/ai-inference-on-aks-arc-part-5"><strong>Triton + TensorRT‑LLM</strong>: optimized large-model inference</a></td><td>Generative</td><td>✅ Available</td></tr><tr><td><strong>Triton + vLLM backend</strong>: vision-language model serving</td><td>Generative</td><td>🔜 Coming soon</td></tr></tbody></table>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="prerequisites">Prerequisites<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h2>
<p>All scenarios in this series run on an AKS enabled by Azure Arc cluster deployed on Azure Local. Before you begin, make sure you have the following in place:</p>
<ul>
<li>
<p><strong>AKS enabled by Azure Arc clusters with a GPU node:</strong> An Azure Local cluster with at least one GPU node and appropriate NVIDIA drivers installed. The GPU node needs the NVIDIA device plugin (via the NVIDIA GPU Operator) running so pods can access nvidia.com/gpu resources.</p>
</li>
<li>
<p><strong>Azure CLI with Azure Arc extensions:</strong> The <a href="https://learn.microsoft.com/cli/azure/install-azure-cli" target="_blank" rel="noopener noreferrer">Azure CLI</a> installed on your admin machine and <code>connectedk8s</code> extensions (for Azure Arc-enabled Kubernetes). Use <code>az extension list -o table</code> to confirm these are installed.</p>
</li>
<li>
<p><strong>kubectl:</strong> The Kubernetes CLI installed on your workstation for applying manifests and managing cluster resources.</p>
</li>
<li>
<p><strong>Helm:</strong> The <a href="https://helm.sh/docs/intro/install/" target="_blank" rel="noopener noreferrer">Helm</a> package manager installed (v3), for deploying the GPU Operator and helm charts as needed.</p>
</li>
<li>
<p><strong>PowerShell&nbsp;7+ (optional):</strong> If using PowerShell for CLI steps and REST calls, upgrade to PowerShell&nbsp;7.4 or later (older Windows PowerShell&nbsp;5.1 may cause JSON quoting issues in our examples).</p>
</li>
<li>
<p><strong>Cluster access:</strong> Ensure you can connect to your AKS enabled by Azure Arc clusters (e.g. same network or VPN to the Azure Local environment). After logging in to Azure and retrieving cluster credentials, verify access by listing nodes:</p>
</li>
</ul>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az login</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Use this command when you have AKS RBAC to export cluster credentials.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az aks get-credentials --resource-group &lt;YourResourceGroup&gt; --name &lt;YourClusterName&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Otherwise, use this command to access the cluster via the proxy without exporting credentials.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az connectedk8s proxy --resource-group &lt;YourResourceGroup&gt; --name &lt;YourClusterName&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># This should show your cluster’s nodes, including any GPU node(s).</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get nodes</span><br></span></code></pre></div></div>
<p>Note: On Windows&nbsp;11, you can use <code>winget</code> to quickly install prerequisites. For example:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Install PowerShell</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget install -e --id Microsoft.PowerShell</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pwsh -v</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Install or Update - Azure CLI, Kubectl, Helm, Git</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget install -e --id Microsoft.AzureCLI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget install -e --id Kubernetes.kubectl</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget install -e --id Helm.Helm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget install -e --id Git.Git</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget update -e --id Microsoft.AzureCLI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget update -e --id Kubernetes.kubectl</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget update -e --id Helm.Helm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">winget update -e --id Git.Git</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Install or Update – Azure CLI Extensions</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az extension add --name aksarc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az extension add --name connectedk8s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az extension update --name aksarc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az extension update --name connectedk8s</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="install-the-nvidia-gpu-operator">Install the NVIDIA GPU operator<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2#install-the-nvidia-gpu-operator" class="hash-link" aria-label="Direct link to Install the NVIDIA GPU operator" title="Direct link to Install the NVIDIA GPU operator" translate="no">​</a></h3>
<p>Next, install the NVIDIA GPU Operator on the cluster. This operator installs the necessary drivers and Kubernetes device plugin to expose GPU resources to your workloads. vLLM requires the NVIDIA Kubernetes plugin to access the GPU hardware.</p>
<ul>
<li><strong>Add the NVIDIA Helm repository:</strong> If you haven’t already, add NVIDIA’s Helm chart repository and update it:</li>
</ul>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">helm repo add nvidia https://helm.ngc.nvidia.com/nvidia</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">helm repo update</span><br></span></code></pre></div></div>
<p>This adds the official NVIDIA chart source (which contains the GPU operator chart) to your Helm client.</p>
<ul>
<li><strong>Install the GPU operator:</strong> Use Helm to install the NVIDIA GPU Operator onto your cluster:</li>
</ul>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">helm install --wait --generate-name nvidia/gpu-operator</span><br></span></code></pre></div></div>
<p>This will install the GPU operator into your cluster (in its default namespace) and wait for all components to be ready. The --generate-name flag automatically assigns a name to the Helm release. The operator will set up the NVIDIA device plugin and drivers on your cluster nodes.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>Ensure your cluster nodes have internet connectivity to pull the necessary container images for the operator. This may take a few minutes the first time as images are downloaded.</p></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="next-up-generative-ai-with-opensource-llm-server">Next up: <a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-3">Generative AI with Open‑Source LLM Server</a><a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2#next-up-generative-ai-with-opensource-llm-server" class="hash-link" aria-label="Direct link to next-up-generative-ai-with-opensource-llm-server" title="Direct link to next-up-generative-ai-with-opensource-llm-server" translate="no">​</a></h3>]]></content:encoded>
            <category>AKS Arc</category>
            <category>AI</category>
            <category>AI Inference</category>
        </item>
        <item>
            <title><![CDATA[AI Inference on AKS enabled by Azure Arc: Bringing AI to the Edge and On‑Premises]]></title>
            <link>https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-1</link>
            <guid>https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-1</guid>
            <pubDate>Tue, 07 Apr 2026 00:01:00 GMT</pubDate>
            <description><![CDATA[Learn how to bring AI inference closer to your data with AKS enabled by Azure Arc, with practical hybrid and edge scenarios for secure, low-latency workloads.]]></description>
            <content:encoded><![CDATA[<p>For many edge and on-premises environments, sending data to the cloud for AI inferencing isn't an option, as latency, data residency, and compliance make it a non-starter. With Azure Kubernetes Service (AKS) enabled by Azure Arc managing your Kubernetes clusters, you can run AI inferencing locally on the hardware you already have. This blog series shows you how, with hands-on tutorials for <strong>experimenting</strong> with generative and predictive AI workloads using CPUs, GPUs, and NPUs.</p>
<p><img decoding="async" loading="lazy" alt="AI Inference on AKS enabled by Azure Arc: Running AI Inference at the Edge and On‑Prem" src="https://blog.aks.azure.com/assets/images/hero-image-838aa762817fa0efebef5db283821711.png" width="888" height="520" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-1#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>Whether you are processing sensor data on a factory floor, analyzing medical images in a hospital, or running in store retail analytics, your AI models need to run where the data lives. With AKS enabled by Azure Arc, you can extend Azure’s Kubernetes management to on‑prem and edge infrastructure and run AI inference without changing how you operate your clusters. You use the same Kubernetes APIs, deployment patterns, and lifecycle workflows across cloud and non‑cloud environments, while keeping inference close to where data is generated. This allows you to operate AI workloads consistently across highly distributed environments.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-ai-inferencing-on-aks-enabled-by-azure-arc-matters">Why AI inferencing on AKS enabled by Azure Arc matters<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-1#why-ai-inferencing-on-aks-enabled-by-azure-arc-matters" class="hash-link" aria-label="Direct link to Why AI inferencing on AKS enabled by Azure Arc matters" title="Direct link to Why AI inferencing on AKS enabled by Azure Arc matters" translate="no">​</a></h2>
<ul>
<li><strong>Low latency and data residency:</strong> Inference runs locally, meeting real-time and compliance requirements for factory automation, medical imaging, and retail analytics.</li>
<li><strong>Existing hardware utilization:</strong> Use your current infrastructure with flexibility to add GPUs or accelerators later.</li>
<li><strong>Hybrid and disconnected operations:</strong> Manage workloads centrally from Azure while local execution continues during network outages.</li>
<li><strong>Industry alignment:</strong> Support the shift toward edge AI driven by data gravity, regulatory compliance, and real-time requirements.</li>
</ul>
<p>Together, these capabilities make AKS enabled by Azure Arc a strong foundation for AI inference across edge and on‑prem environments, enabling you to choose and operate inference engines and models directly on your Kubernetes clusters, including bring‑your‑own models, while integrating with Microsoft’s broader AI stack such as <a href="https://learn.microsoft.com/azure/foundry/what-is-foundry" target="_blank" rel="noopener noreferrer">Microsoft Foundry</a>, <a href="https://learn.microsoft.com/azure/foundry-local/what-is-foundry-local" target="_blank" rel="noopener noreferrer">Microsoft Foundry Local</a>, and <a href="https://learn.microsoft.com/azure/aks/ai-toolchain-operator" target="_blank" rel="noopener noreferrer">KAITO</a> where appropriate.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="get-started">Get started<a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-1#get-started" class="hash-link" aria-label="Direct link to Get started" title="Direct link to Get started" translate="no">​</a></h2>
<p>This series walks you through experimenting with generative and predictive AI workloads step by step, using open-source tools and real models on your AKS enabled by Azure Arc clusters. For the full list of topics, prerequisites, and hands-on tutorials, head to the <a href="https://blog.aks.azure.com/2026/04/07/ai-inference-on-aks-arc-part-2">Series Introduction and Scope</a>.</p>]]></content:encoded>
            <category>AKS Arc</category>
            <category>AI</category>
            <category>AI Inference</category>
        </item>
        <item>
            <title><![CDATA[Optimizing RDMA performance for AI workloads on AKS with DRANET]]></title>
            <link>https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks</link>
            <guid>https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks</guid>
            <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Use DRANET and DRA on AKS to boost RDMA performance with topology-aware scheduling, GPU/NIC alignment, and fast placement for AI training and inferencing.]]></description>
            <content:encoded><![CDATA[<p>RDMA (Remote Direct Memory Access) is critical for unlocking the full potential of GPU infrastructure, enabling the high-throughput, low-latency GPU-to-GPU communication that large-scale AI workloads demand. In distributed training, collective operations like all-reduce and all-gather synchronize gradients and activations across GPUs — any communication bottleneck stalls the entire training pipeline. In disaggregated inference, RDMA provides the fast inter-node transfers needed to move KV-cache data between prefill and decode phases running on separate GPU pools.</p>
<p><img decoding="async" loading="lazy" alt="rdma-for-ai-workload-on-gpu-infra" src="https://blog.aks.azure.com/assets/images/rdma-for-gpu-infra-ai-workload-377cbc312810f9abfda18156e473d921.png" width="1408" height="768" class="img_ev3q"></p>
<p><a href="https://github.com/kubernetes-sigs/dranet" target="_blank" rel="noopener noreferrer">DRANET</a> is an open-source <a href="https://kubernetes.io/docs/concepts/scheduling-eviction/dynamic-resource-allocation/" target="_blank" rel="noopener noreferrer">Dynamic Resource Allocation</a> (DRA) network driver that discovers RDMA-capable devices, advertises them as ResourceSlices, and injects the allocated devices into each pod and container. Combined with the <a href="https://github.com/kubernetes-sigs/nvidia-dra-driver-gpu" target="_blank" rel="noopener noreferrer">NVIDIA GPU DRA driver</a>, it enables topology-aware co-scheduling of GPUs and NICs for high-performance AI networking on Kubernetes.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>The NVIDIA GPU DRA driver and DRANET need be installed by users on their AKS clusters. A managed AKS solution is currently working in progress. For a deeper walkthrough of DRA concepts and a hands-on tutorial with the NVIDIA GPU DRA driver, see our previous post on <a href="https://blog.aks.azure.com/2025/11/17/dra-devices-and-drivers-on-kubernetes">DRA devices and drivers on Kubernetes</a>.</p></div></div>
<p>In this post, you’ll learn how DRANET works on <a href="https://kubernetes.io/blog/2025/09/01/kubernetes-v1-34-dra-updates/" target="_blank" rel="noopener noreferrer">AKS 1.34</a> with <a href="https://learn.microsoft.com/azure/virtual-machines/sizes/gpu-accelerated/nd-gb300-v6-series?tabs=sizebasic" target="_blank" rel="noopener noreferrer">Azure ND GB300-v6</a> nodes, demonstrate three NUMA (Non-Uniform Memory Access) alignment scenarios, show and compare the RDMA benchmark results.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="naive-scheduling-hurts-performance">Naive scheduling hurts performance<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#naive-scheduling-hurts-performance" class="hash-link" aria-label="Direct link to Naive scheduling hurts performance" title="Direct link to Naive scheduling hurts performance" translate="no">​</a></h2>
<p>On an Azure ND GB300-v6 node, there are four <a href="https://www.nvidia.com/en-us/data-center/gb300-nvl72/" target="_blank" rel="noopener noreferrer">NVIDIA GB300</a> GPUs and four <a href="https://www.nvidia.com/en-us/networking/infiniband-adapters/" target="_blank" rel="noopener noreferrer">NVIDIA ConnectX-8</a> NICs with <a href="https://www.nvidia.com/en-us/networking/products/infiniband/" target="_blank" rel="noopener noreferrer">InfiniBand</a> spread across two NUMA domains. The hardware topology looks like this:</p>
<table><thead><tr><th>Resource</th><th>Count</th><th>Detail</th></tr></thead><tbody><tr><td>GPU</td><td>4x NVIDIA GB300</td><td>288 GB HBM3E each</td></tr><tr><td>NIC</td><td>4x NVIDIA ConnectX-8</td><td>100 GB/s InfiniBand each</td></tr><tr><td>NUMA nodes</td><td>2</td><td>2 GPUs + 2 NICs per NUMA node</td></tr></tbody></table>
<p>The NUMA topology from <code>nvidia-smi topo -m</code> reveals the affinity relationships:</p>
<table><thead><tr><th></th><th>GPU0</th><th>GPU1</th><th>GPU2</th><th>GPU3</th><th>NIC0</th><th>NIC1</th><th>NIC2</th><th>NIC3</th></tr></thead><tbody><tr><td>GPU0</td><td>X</td><td>NV18</td><td>NV18</td><td>NV18</td><td>NODE</td><td>NODE</td><td>SYS</td><td>SYS</td></tr><tr><td>GPU1</td><td>NV18</td><td>X</td><td>NV18</td><td>NV18</td><td>NODE</td><td>NODE</td><td>SYS</td><td>SYS</td></tr><tr><td>GPU2</td><td>NV18</td><td>NV18</td><td>X</td><td>NV18</td><td>SYS</td><td>SYS</td><td>NODE</td><td>NODE</td></tr><tr><td>GPU3</td><td>NV18</td><td>NV18</td><td>NV18</td><td>X</td><td>SYS</td><td>SYS</td><td>NODE</td><td>NODE</td></tr></tbody></table>
<p>GPUs 0-1 and NICs 0-1 share NUMA node 0. GPUs 2-3 and NICs 2-3 share NUMA node 1. A <strong>NODE</strong> relationship means the GPU and NIC share a direct PCIe root complex, enabling GPU-Direct RDMA (GDR). A <strong>SYS</strong> relationship means data must cross the QPI/UPI interconnect between NUMA domains, disabling GDR and adding latency.</p>
<p>Without topology-aware scheduling, Kubernetes has no way to co-locate a GPU and its NUMA-local NICs in the same ResourceClaim. Scheduling a workload onto a GPU with the wrong NIC on a different NUMA node can silently result in slower data paths and degrade RDMA performance.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-dranet-works">How DRANET works<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#how-dranet-works" class="hash-link" aria-label="Direct link to How DRANET works" title="Direct link to How DRANET works" translate="no">​</a></h2>
<p>The following system diagrams show how the DRA and DRANET control plane and data plane components work together to achieve topology-aware GPU and NIC alignment.</p>
<p><strong>Control plane</strong>: DRA drivers discover hardware topology and publish ResourceSlices. The scheduler evaluates <a href="https://kubernetes.io/docs/reference/using-api/cel/" target="_blank" rel="noopener noreferrer">Common Expression Language</a> (CEL) selectors from ResourceClaimTemplates to allocate NUMA-aligned GPU and NIC pairs.</p>
<p><img decoding="async" loading="lazy" alt="Control plane diagram showing DRA drivers publishing device topology to ResourceSlices, and the scheduler evaluating CEL selectors to allocate NUMA-aligned GPU and NIC pairs" src="https://blog.aks.azure.com/assets/images/control-plane-diagram-30fac9a16ddd8ba7ea9bd2cdee7003c3.svg" width="1397" height="972" class="img_ev3q"></p>
<p><strong>Data plane</strong>: Once the scheduler binds a pod with ResourceClaim, the kubelet instructs containerd to create the container. The <a href="https://github.com/containerd/nri" target="_blank" rel="noopener noreferrer">Node Resource Interface</a> (NRI) plugin intercepts the OCI hook and injects the allocated RDMA devices, enabling GPU-Direct RDMA over NUMA-local PCIe paths.</p>
<p><img decoding="async" loading="lazy" alt="Data plane diagram showing kubelet, containerd, NRI plugin injecting allocated RDMA devices into the pod, with NUMA-aligned GPU-NIC PCIe GDR paths" src="https://blog.aks.azure.com/assets/images/data-plane-diagram-210a84a9331c22ae25385548ac651dec.svg" width="1246" height="1050" class="img_ev3q"></p>
<p>That high-level flow depends on node-local DRANET agents to turn scheduling decisions into usable RDMA resources. DRANET runs as a DaemonSet on each node and handles three key tasks:</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-discovering-rdma-devices">1. Discovering RDMA devices<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#1-discovering-rdma-devices" class="hash-link" aria-label="Direct link to 1. Discovering RDMA devices" title="Direct link to 1. Discovering RDMA devices" translate="no">​</a></h3>
<p>The NVIDIA ConnectX-8 NICs on Azure ND GB300-v6 nodes operate in InfiniBand mode -- they have no Ethernet netdev interface. DRANET discovers them by:</p>
<ul>
<li>Skipping IPoIB interfaces during netdev discovery</li>
<li>Recording the RDMA link name (<code>rdmaDevice</code>) on each PCI device</li>
<li>Identifying IB-only devices as those with a non-empty <code>rdmaDevice</code> and no <code>ifName</code></li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-publishing-dranet-resourceslices">2. Publishing DRANET ResourceSlices<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#2-publishing-dranet-resourceslices" class="hash-link" aria-label="Direct link to 2. Publishing DRANET ResourceSlices" title="Direct link to 2. Publishing DRANET ResourceSlices" translate="no">​</a></h3>
<p>DRANET publishes a ResourceSlice for each node, exposing every discovered NIC as a named device with topology attributes that the scheduler can match against:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">devices</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> pci</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">0101</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">00</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">00</span><span class="token punctuation" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">attributes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">dra.net/numaNode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">int</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">dra.net/pciAddress</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">string</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0101:00:00.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">dra.net/rdma</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">bool</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">dra.net/rdmaDevice</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">string</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mlx5_0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">driver</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> dra.net</span><br></span></code></pre></div></div>
<p>The NVIDIA GPU DRA driver (<code>gpu.nvidia.com</code>) similarly publishes GPU attributes including <code>pciBusID</code> and <code>numaNode</code>. Together, these attributes give the Kubernetes scheduler enough information to make NUMA-aligned allocation decisions.</p>
<p>Here are the full device inventories on Azure ND GB300 node:</p>
<p><strong>GPUs</strong> (driver: <code>gpu.nvidia.com</code>):</p>
<table><thead><tr><th>Device</th><th>pciBusID</th><th>NUMA</th><th>pcieRoot</th></tr></thead><tbody><tr><td>gpu-0</td><td>0008:06:00.0</td><td>0</td><td>pci0008:00</td></tr><tr><td>gpu-1</td><td>0009:06:00.0</td><td>0</td><td>pci0009:00</td></tr><tr><td>gpu-2</td><td>0018:06:00.0</td><td>1</td><td>pci0018:00</td></tr><tr><td>gpu-3</td><td>0019:06:00.0</td><td>1</td><td>pci0019:00</td></tr></tbody></table>
<p><strong>NICs</strong> (driver: <code>dra.net</code>):</p>
<table><thead><tr><th>Device</th><th>pciAddress</th><th>rdmaDevice</th><th>NUMA</th></tr></thead><tbody><tr><td>pci-0101-00-00-0</td><td>0101:00:00.0</td><td>mlx5_0</td><td>0</td></tr><tr><td>pci-0102-00-00-0</td><td>0102:00:00.0</td><td>mlx5_1</td><td>0</td></tr><tr><td>pci-0103-00-00-0</td><td>0103:00:00.0</td><td>mlx5_2</td><td>1</td></tr><tr><td>pci-0104-00-00-0</td><td>0104:00:00.0</td><td>mlx5_3</td><td>1</td></tr></tbody></table>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="3-injecting-rdma-devices">3. Injecting RDMA Devices<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#3-injecting-rdma-devices" class="hash-link" aria-label="Direct link to 3. Injecting RDMA Devices" title="Direct link to 3. Injecting RDMA Devices" translate="no">​</a></h3>
<p>At pod start, the NRI plugin injects only the allocated <code>/dev/infiniband/uverbsN</code> character devices into containers, to give each pod visibility into exactly the devices it was allocated. This also reduces the security risk of setting <code>privileged: true</code>, which grants containers far more permissions than workloads actually need to leverage RDMA.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-to-use-dranet">How to use DRANET<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#how-to-use-dranet" class="hash-link" aria-label="Direct link to How to use DRANET" title="Direct link to How to use DRANET" translate="no">​</a></h2>
<p>With ResourceSlices published, workload authors can write ResourceClaimTemplates that use CEL selectors to express precise GPU-NIC alignment constraints. Each template creates a per-pod ResourceClaim that requests devices from both the <code>gpu.nvidia.com</code> and <code>dranet.net</code> DeviceClasses, filtered by attributes like device name  or PCI address. We define three ResourceClaimTemplates to demonstrate different alignment strategies.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1nic-unaligned-1-gpu--1-nic-cross-numa">1nic-unaligned: 1 GPU + 1 NIC, cross-NUMA<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#1nic-unaligned-1-gpu--1-nic-cross-numa" class="hash-link" aria-label="Direct link to 1nic-unaligned: 1 GPU + 1 NIC, cross-NUMA" title="Direct link to 1nic-unaligned: 1 GPU + 1 NIC, cross-NUMA" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> resource.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ResourceClaimTemplate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 1nic</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">unaligned</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">devices</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gpu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">exactly</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">deviceClassName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gpu.nvidia.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">count</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">selectors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">cel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">expression</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                device.attributes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"resource.kubernetes.io"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">.pciBusID == "0008</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">06</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">00.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">exactly</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">deviceClassName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> dranet.net</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">count</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">selectors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">cel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">expression</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                device.attributes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"dra.net"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"rdmaDevice"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> == "mlx5_2"</span><br></span></code></pre></div></div>
<p>GPU 0 (NUMA 0) is paired with mlx5_2 (NUMA 1) with <strong>SYS</strong> affinity, meaning cross-NUMA with no GDR path. This serves as the baseline.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1nic-aligned-1-gpu--1-nic-same-numa">1nic-aligned: 1 GPU + 1 NIC, same NUMA<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#1nic-aligned-1-gpu--1-nic-same-numa" class="hash-link" aria-label="Direct link to 1nic-aligned: 1 GPU + 1 NIC, same NUMA" title="Direct link to 1nic-aligned: 1 GPU + 1 NIC, same NUMA" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> resource.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ResourceClaimTemplate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 1nic</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">aligned</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">devices</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gpu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">selectors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">cel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">expression</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                device.attributes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"resource.kubernetes.io"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">.pciBusID == "0008</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">06</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">00.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">selectors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">cel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">expression</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                device.attributes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"dra.net"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"rdmaDevice"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> == "mlx5_0"</span><br></span></code></pre></div></div>
<p>GPU 0 (NUMA 0) is paired with mlx5_0 (NUMA 0) with <strong>NODE</strong> affinity and a direct PCIe path for GDR.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2nic-aligned-1-gpu--2-nics-same-numa">2nic-aligned: 1 GPU + 2 NICs, same NUMA<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#2nic-aligned-1-gpu--2-nics-same-numa" class="hash-link" aria-label="Direct link to 2nic-aligned: 1 GPU + 2 NICs, same NUMA" title="Direct link to 2nic-aligned: 1 GPU + 2 NICs, same NUMA" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> resource.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ResourceClaimTemplate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 2nic</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">aligned</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">devices</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gpu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">selectors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">cel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">expression</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                device.attributes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"resource.kubernetes.io"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">.pciBusID == "0008</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">06</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">00.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">selectors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">cel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">expression</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                device.attributes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"dra.net"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"rdma"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> == true </span><span class="token important">&amp;&amp;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                device.attributes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"dra.net"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"numaNode"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> == 0</span><br></span></code></pre></div></div>
<p>GPU 0 (NUMA 0) is paired with both RDMA NICs mlx5_0 + mlx5_1 (NUMA 0). The <code>count: 2</code> with a NUMA selector is the idiomatic DRA pattern for multi-device allocation from a homogeneous group.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="benchmark-and-comparison">Benchmark and Comparison<a href="https://blog.aks.azure.com/2026/04/01/dranet-rdma-optimization-for-ai-on-aks#benchmark-and-comparison" class="hash-link" aria-label="Direct link to Benchmark and Comparison" title="Direct link to Benchmark and Comparison" translate="no">​</a></h2>
<p>The RDMA benchmark uses an MPIJob from the Kubeflow MPI Operator to run NCCL <code>all_reduce_perf</code> across two worker pods on different nodes, each with one GPU and one or more NICs allocated through DRA. Here is one example MPIJob yaml file:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kubeflow.org/v2beta1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> MPIJob</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nccl</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">test</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">dra</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">mpiReplicaSpecs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">Launcher</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> launcher</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"/bin/bash"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"-c"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">args</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              mpirun -np 2 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                -x NCCL_IB_DISABLE=0 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                -x NCCL_SHM_DISABLE=1 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                -x NCCL_MNNVL_ENABLE=0 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                -x NCCL_NVLS_ENABLE=0 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                /opt/nccl_tests/build/all_reduce_perf \</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                  -b 512M -e 8G -f 2 -g 1 -c 0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">Worker</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">affinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">podAntiAffinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">requiredDuringSchedulingIgnoredDuringExecution</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token key atrule" style="color:#00a4db">training.kubeflow.org/job-name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nccl</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">test</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">dra</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token key atrule" style="color:#00a4db">training.kubeflow.org/job-role</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> worker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">topologyKey</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kubernetes.io/hostname</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">resourceClaims</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gpu</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">nic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">resourceClaimTemplateName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 1nic</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">aligned</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> worker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">claims</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gpu</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">nic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">...</span><br></span></code></pre></div></div>
<p>There are a few pieces worth calling out:</p>
<p><strong>NCCL environment variables force pure InfiniBand transport.</strong> The launcher pod sets <code>NCCL_IB_DISABLE=0</code> to explicitly enable the InfiniBand transport, <code>NCCL_SHM_DISABLE=1</code> to prevent NCCL from using shared memory for intra-node transfers, <code>NCCL_MNNVL_ENABLE=0</code> to disable multi-node NVLink (MNNVL), and <code>NCCL_NVLS_ENABLE=0</code> to disable NVLink SHARP (NVLS) so that collectives don't offload to NVSwitch hardware. Together, these flags force all collective traffic through the InfiniBand transport, so the results reflect pure RDMA throughput.</p>
<p><strong>Pod anti-affinity forces inter-node RDMA.</strong> The <code>podAntiAffinity</code> rule uses <code>requiredDuringSchedulingIgnoredDuringExecution</code> with <code>topologyKey: kubernetes.io/hostname</code> to guarantee that the two worker replicas land on different nodes. Without this, the scheduler could place both workers on the same node, where GPUs communicate intra-node instead of over InfiniBand fabric.</p>
<p><strong><code>resourceClaimTemplateName</code> selects the GPU-NIC topology.</strong> Each worker pod references a single ResourceClaimTemplate through the <code>resourceClaimTemplateName</code> field under <code>resourceClaims</code>. To switch between alignment scenarios, change this field from <code>1nic-aligned</code> to <code>2nic-aligned</code> or <code>1nic-unaligned</code> -- the scheduler will then allocate a different set of GPU and NIC devices based on the CEL selectors defined in each template. This is the only line that needs to change between benchmark runs.</p>
<p>The following chart shows the benchmark results across the three ResourceClaimTemplates:</p>
<p><img decoding="async" loading="lazy" alt="Bar chart comparing NCCL all_reduce_perf average bus bandwidth across three NUMA placement scenarios: 1nic-unaligned at 25 GB/s, 1nic-aligned at 56 GB/s, and 2nic-aligned at 112 GB/s" src="data:image/svg+xml;base64,PHN2ZyBpZD0ibXktc3ZnIiB3aWR0aD0iMTAwJSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgc3R5bGU9Im1heC13aWR0aDogNzYwcHg7IGJhY2tncm91bmQtY29sb3I6IHRyYW5zcGFyZW50OyIgdmlld0JveD0iMCAwIDc2MCA1MDAiIHJvbGU9ImdyYXBoaWNzLWRvY3VtZW50IGRvY3VtZW50IiBhcmlhLXJvbGVkZXNjcmlwdGlvbj0ieHljaGFydCI+PHN0eWxlPiNteS1zdmd7Zm9udC1mYW1pbHk6InRyZWJ1Y2hldCBtcyIsdmVyZGFuYSxhcmlhbCxzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZpbGw6IzMzMzt9QGtleWZyYW1lcyBlZGdlLWFuaW1hdGlvbi1mcmFtZXtmcm9te3N0cm9rZS1kYXNob2Zmc2V0OjA7fX1Aa2V5ZnJhbWVzIGRhc2h7dG97c3Ryb2tlLWRhc2hvZmZzZXQ6MDt9fSNteS1zdmcgLmVkZ2UtYW5pbWF0aW9uLXNsb3d7c3Ryb2tlLWRhc2hhcnJheTo5LDUhaW1wb3J0YW50O3N0cm9rZS1kYXNob2Zmc2V0OjkwMDthbmltYXRpb246ZGFzaCA1MHMgbGluZWFyIGluZmluaXRlO3N0cm9rZS1saW5lY2FwOnJvdW5kO30jbXktc3ZnIC5lZGdlLWFuaW1hdGlvbi1mYXN0e3N0cm9rZS1kYXNoYXJyYXk6OSw1IWltcG9ydGFudDtzdHJva2UtZGFzaG9mZnNldDo5MDA7YW5pbWF0aW9uOmRhc2ggMjBzIGxpbmVhciBpbmZpbml0ZTtzdHJva2UtbGluZWNhcDpyb3VuZDt9I215LXN2ZyAuZXJyb3ItaWNvbntmaWxsOmhzbCg4Ni43MTUzMjg0NjcyLCA3Ny40MDExMjk5NDM1JSwgNzAuMjk0MTE3NjQ3MSUpO30jbXktc3ZnIC5lcnJvci10ZXh0e2ZpbGw6cmdiKDY5LjMzMDUwODQ3NDYsIDE3LjExODY0NDA2NzgsIDEzNC4zODEzNTU5MzIpO3N0cm9rZTpyZ2IoNjkuMzMwNTA4NDc0NiwgMTcuMTE4NjQ0MDY3OCwgMTM0LjM4MTM1NTkzMik7fSNteS1zdmcgLmVkZ2UtdGhpY2tuZXNzLW5vcm1hbHtzdHJva2Utd2lkdGg6MXB4O30jbXktc3ZnIC5lZGdlLXRoaWNrbmVzcy10aGlja3tzdHJva2Utd2lkdGg6My41cHg7fSNteS1zdmcgLmVkZ2UtcGF0dGVybi1zb2xpZHtzdHJva2UtZGFzaGFycmF5OjA7fSNteS1zdmcgLmVkZ2UtdGhpY2tuZXNzLWludmlzaWJsZXtzdHJva2Utd2lkdGg6MDtmaWxsOm5vbmU7fSNteS1zdmcgLmVkZ2UtcGF0dGVybi1kYXNoZWR7c3Ryb2tlLWRhc2hhcnJheTozO30jbXktc3ZnIC5lZGdlLXBhdHRlcm4tZG90dGVke3N0cm9rZS1kYXNoYXJyYXk6Mjt9I215LXN2ZyAubWFya2Vye2ZpbGw6IzBiMGIwYjtzdHJva2U6IzBiMGIwYjt9I215LXN2ZyAubWFya2VyLmNyb3Nze3N0cm9rZTojMGIwYjBiO30jbXktc3ZnIHN2Z3tmb250LWZhbWlseToidHJlYnVjaGV0IG1zIix2ZXJkYW5hLGFyaWFsLHNhbnMtc2VyaWY7Zm9udC1zaXplOjE2cHg7fSNteS1zdmcgcHttYXJnaW46MDt9I215LXN2ZyAubm9kZSAubmVvLW5vZGV7c3Ryb2tlOmhzbCgyNjYuNzE1MzI4NDY3MiwgMzcuNDAxMTI5OTQzNSUsIDU1LjI5NDExNzY0NzElKTt9I215LXN2ZyBbZGF0YS1sb29rPSJuZW8iXS5ub2RlIHJlY3QsI215LXN2ZyBbZGF0YS1sb29rPSJuZW8iXS5jbHVzdGVyIHJlY3QsI215LXN2ZyBbZGF0YS1sb29rPSJuZW8iXS5ub2RlIHBvbHlnb257c3Ryb2tlOnVybCgjbXktc3ZnLWdyYWRpZW50KTtmaWx0ZXI6ZHJvcC1zaGFkb3coIDFweCAycHggMnB4IHJnYmEoMTg1LDE4NSwxODUsMSkpO30jbXktc3ZnIFtkYXRhLWxvb2s9Im5lbyJdLm5vZGUgcGF0aHtzdHJva2U6dXJsKCNteS1zdmctZ3JhZGllbnQpO3N0cm9rZS13aWR0aDoxcHg7fSNteS1zdmcgW2RhdGEtbG9vaz0ibmVvIl0ubm9kZSAub3V0ZXItcGF0aHtmaWx0ZXI6ZHJvcC1zaGFkb3coIDFweCAycHggMnB4IHJnYmEoMTg1LDE4NSwxODUsMSkpO30jbXktc3ZnIFtkYXRhLWxvb2s9Im5lbyJdLm5vZGUgLm5lby1saW5lIHBhdGh7c3Ryb2tlOmhzbCgyNjYuNzE1MzI4NDY3MiwgMzcuNDAxMTI5OTQzNSUsIDU1LjI5NDExNzY0NzElKTtmaWx0ZXI6bm9uZTt9I215LXN2ZyBbZGF0YS1sb29rPSJuZW8iXS5ub2RlIGNpcmNsZXtzdHJva2U6dXJsKCNteS1zdmctZ3JhZGllbnQpO2ZpbHRlcjpkcm9wLXNoYWRvdyggMXB4IDJweCAycHggcmdiYSgxODUsMTg1LDE4NSwxKSk7fSNteS1zdmcgW2RhdGEtbG9vaz0ibmVvIl0ubm9kZSBjaXJjbGUgLnN0YXRlLXN0YXJ0e2ZpbGw6IzAwMDAwMDt9I215LXN2ZyBbZGF0YS1sb29rPSJuZW8iXS5pY29uLXNoYXBlIC5pY29ue2ZpbGw6dXJsKCNteS1zdmctZ3JhZGllbnQpO2ZpbHRlcjpkcm9wLXNoYWRvdyggMXB4IDJweCAycHggcmdiYSgxODUsMTg1LDE4NSwxKSk7fSNteS1zdmcgW2RhdGEtbG9vaz0ibmVvIl0uaWNvbi1zaGFwZSAuaWNvbi1uZW8gcGF0aHtzdHJva2U6dXJsKCNteS1zdmctZ3JhZGllbnQpO2ZpbHRlcjpkcm9wLXNoYWRvdyggMXB4IDJweCAycHggcmdiYSgxODUsMTg1LDE4NSwxKSk7fSNteS1zdmcgOnJvb3R7LS1tZXJtYWlkLWZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjt9PC9zdHlsZT48Zy8+PGcgY2xhc3M9Im1haW4iPjxyZWN0IHdpZHRoPSI3NjAiIGhlaWdodD0iNTAwIiBjbGFzcz0iYmFja2dyb3VuZCIgZmlsbD0iI2ZmZmZmZiIvPjxnIGNsYXNzPSJjaGFydC10aXRsZSI+PHRleHQgeD0iMCIgeT0iMCIgZmlsbD0iIzMzMyIgZm9udC1zaXplPSIyMCIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzUwLCAyMikgcm90YXRlKDApIj5OQ0NMIGFsbF9yZWR1Y2VfcGVyZiDigJQgQXZnIEJ1cyBCYW5kd2lkdGggKEdCL3MpPC90ZXh0PjwvZz48ZyBjbGFzcz0icGxvdCI+PGcgY2xhc3M9ImJhci1wbG90LTAiPjxyZWN0IHg9Ijc2LjIzNDM3NjkwNzM0ODYzIiB5PSIzNzQuMjA4MzMzMzMzMzMzMyIgd2lkdGg9IjEzMyIgaGVpZ2h0PSI5Mi43OTE2NjY2NjY2NjY2OSIgZmlsbD0iIzlmNjJlYiIgc3Ryb2tlPSIjOWY2MmViIiBzdHJva2Utd2lkdGg9IjAiLz48cmVjdCB4PSIzMTkuODY3MTg4NDUzNjc0MyIgeT0iMjY5LjA2NjY2NjY2NjY2NjY2IiB3aWR0aD0iMTMzIiBoZWlnaHQ9IjE5Ny45MzMzMzMzMzMzMzMzNCIgZmlsbD0iIzlmNjJlYiIgc3Ryb2tlPSIjOWY2MmViIiBzdHJva2Utd2lkdGg9IjAiLz48cmVjdCB4PSI1NjMuNSIgeT0iNzkuMTMzMzMzMzMzMzMzMzMiIHdpZHRoPSIxMzMiIGhlaWdodD0iMzg3Ljg2NjY2NjY2NjY2NjciIGZpbGw9IiM5ZjYyZWIiIHN0cm9rZT0iIzlmNjJlYiIgc3Ryb2tlLXdpZHRoPSIwIi8+PC9nPjwvZz48ZyBjbGFzcz0iYmFyLWFubm90YXRpb25zIj48dGV4dCB4PSIxNDIuNzMiIHk9IjM0OCIgZmlsbD0iIzFhMWExYSIgZm9udC1zaXplPSIxMiIgZm9udC13ZWlnaHQ9ImJvbGQiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjI1IEdCL3M8L3RleHQ+PHRleHQgeD0iMTQyLjczIiB5PSIzNjIiIGZpbGw9IiM1NTUiIGZvbnQtc2l6ZT0iMTEiIHRleHQtYW5jaG9yPSJtaWRkbGUiPmNyb3NzLU5VTUEsIG5vIEdEUjwvdGV4dD48dGV4dCB4PSIzODYuMzciIHk9IjI0MyIgZmlsbD0iIzFhMWExYSIgZm9udC1zaXplPSIxMiIgZm9udC13ZWlnaHQ9ImJvbGQiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjU2IEdCL3M8L3RleHQ+PHRleHQgeD0iMzg2LjM3IiB5PSIyNTciIGZpbGw9IiM1NTUiIGZvbnQtc2l6ZT0iMTEiIHRleHQtYW5jaG9yPSJtaWRkbGUiPnNhbWUgTlVNQSwgR0RSPC90ZXh0Pjx0ZXh0IHg9IjYzMCIgeT0iNTMiIGZpbGw9IiMxYTFhMWEiIGZvbnQtc2l6ZT0iMTIiIGZvbnQtd2VpZ2h0PSJib2xkIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4xMTIgR0IvczwvdGV4dD48dGV4dCB4PSI2MzAiIHk9IjY3IiBmaWxsPSIjNTU1IiBmb250LXNpemU9IjExIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5zYW1lIE5VTUEsIEdEUjwvdGV4dD48L2c+PGcgY2xhc3M9ImJvdHRvbS1heGlzIj48ZyBjbGFzcz0iYXhpcy1saW5lIj48cGF0aCBkPSJNIDcyLjczNDM3NjkwNzM0ODYzLDQ2OCBMIDc2MCw0NjgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9nPjxnIGNsYXNzPSJsYWJlbCI+PHRleHQgeD0iMCIgeT0iMCIgZmlsbD0iIzMzMyIgZm9udC1zaXplPSIxNCIgZG9taW5hbnQtYmFzZWxpbmU9InRleHQtYmVmb3JlLWVkZ2UiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE0Mi43MzQzNzY5MDczNDg2MywgNDc5KSByb3RhdGUoMCkiPjFuaWMtdW5hbGlnbmVkPC90ZXh0Pjx0ZXh0IHg9IjAiIHk9IjAiIGZpbGw9IiMzMzMiIGZvbnQtc2l6ZT0iMTQiIGRvbWluYW50LWJhc2VsaW5lPSJ0ZXh0LWJlZm9yZS1lZGdlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzODYuMzY3MTg4NDUzNjc0MywgNDc5KSByb3RhdGUoMCkiPjFuaWMtYWxpZ25lZDwvdGV4dD48dGV4dCB4PSIwIiB5PSIwIiBmaWxsPSIjMzMzIiBmb250LXNpemU9IjE0IiBkb21pbmFudC1iYXNlbGluZT0idGV4dC1iZWZvcmUtZWRnZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjMwLCA0NzkpIHJvdGF0ZSgwKSI+Mm5pYy1hbGlnbmVkPC90ZXh0PjwvZz48ZyBjbGFzcz0idGlja3MiPjxwYXRoIGQ9Ik0gMTQyLjczNDM3NjkwNzM0ODYzLDQ2OSBMIDE0Mi43MzQzNzY5MDczNDg2Myw0NzQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTSAzODYuMzY3MTg4NDUzNjc0Myw0NjkgTCAzODYuMzY3MTg4NDUzNjc0Myw0NzQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTSA2MzAsNDY5IEwgNjMwLDQ3NCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiLz48L2c+PC9nPjxnIGNsYXNzPSJsZWZ0LWF4aXMiPjxnIGNsYXNzPSJheGlzbC1saW5lIj48cGF0aCBkPSJNIDcxLjczNDM3NjkwNzM0ODYzLDQ0IEwgNzEuNzM0Mzc2OTA3MzQ4NjMsNDY3IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMiIvPjwvZz48ZyBjbGFzcz0ibGFiZWwiPjx0ZXh0IHg9IjAiIHk9IjAiIGZpbGw9IiMzMzMiIGZvbnQtc2l6ZT0iMTQiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJlbmQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYwLjczNDM3NjkwNzM0ODYzLCA1Mikgcm90YXRlKDApIj4xMjA8L3RleHQ+PHRleHQgeD0iMCIgeT0iMCIgZmlsbD0iIzMzMyIgZm9udC1zaXplPSIxNCIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSIgdGV4dC1hbmNob3I9ImVuZCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjAuNzM0Mzc2OTA3MzQ4NjMsIDg1LjkxNjY2NjY2NjY2NjY5KSByb3RhdGUoMCkiPjExMDwvdGV4dD48dGV4dCB4PSIwIiB5PSIwIiBmaWxsPSIjMzMzIiBmb250LXNpemU9IjE0IiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiB0ZXh0LWFuY2hvcj0iZW5kIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MC43MzQzNzY5MDczNDg2MywgMTE5LjgzMzMzMzMzMzMzMzMxKSByb3RhdGUoMCkiPjEwMDwvdGV4dD48dGV4dCB4PSIwIiB5PSIwIiBmaWxsPSIjMzMzIiBmb250LXNpemU9IjE0IiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiB0ZXh0LWFuY2hvcj0iZW5kIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MC43MzQzNzY5MDczNDg2MywgMTUzLjc1KSByb3RhdGUoMCkiPjkwPC90ZXh0Pjx0ZXh0IHg9IjAiIHk9IjAiIGZpbGw9IiMzMzMiIGZvbnQtc2l6ZT0iMTQiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJlbmQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYwLjczNDM3NjkwNzM0ODYzLCAxODcuNjY2NjY2NjY2NjY2NjkpIHJvdGF0ZSgwKSI+ODA8L3RleHQ+PHRleHQgeD0iMCIgeT0iMCIgZmlsbD0iIzMzMyIgZm9udC1zaXplPSIxNCIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSIgdGV4dC1hbmNob3I9ImVuZCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjAuNzM0Mzc2OTA3MzQ4NjMsIDIyMS41ODMzMzMzMzMzMzMzMSkgcm90YXRlKDApIj43MDwvdGV4dD48dGV4dCB4PSIwIiB5PSIwIiBmaWxsPSIjMzMzIiBmb250LXNpemU9IjE0IiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiB0ZXh0LWFuY2hvcj0iZW5kIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MC43MzQzNzY5MDczNDg2MywgMjU1LjUpIHJvdGF0ZSgwKSI+NjA8L3RleHQ+PHRleHQgeD0iMCIgeT0iMCIgZmlsbD0iIzMzMyIgZm9udC1zaXplPSIxNCIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSIgdGV4dC1hbmNob3I9ImVuZCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjAuNzM0Mzc2OTA3MzQ4NjMsIDI4OS40MTY2NjY2NjY2NjY2Mykgcm90YXRlKDApIj41MDwvdGV4dD48dGV4dCB4PSIwIiB5PSIwIiBmaWxsPSIjMzMzIiBmb250LXNpemU9IjE0IiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiB0ZXh0LWFuY2hvcj0iZW5kIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MC43MzQzNzY5MDczNDg2MywgMzIzLjMzMzMzMzMzMzMzMzM3KSByb3RhdGUoMCkiPjQwPC90ZXh0Pjx0ZXh0IHg9IjAiIHk9IjAiIGZpbGw9IiMzMzMiIGZvbnQtc2l6ZT0iMTQiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJlbmQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYwLjczNDM3NjkwNzM0ODYzLCAzNTcuMjUpIHJvdGF0ZSgwKSI+MzA8L3RleHQ+PHRleHQgeD0iMCIgeT0iMCIgZmlsbD0iIzMzMyIgZm9udC1zaXplPSIxNCIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSIgdGV4dC1hbmNob3I9ImVuZCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjAuNzM0Mzc2OTA3MzQ4NjMsIDM5MS4xNjY2NjY2NjY2NjY3KSByb3RhdGUoMCkiPjIwPC90ZXh0Pjx0ZXh0IHg9IjAiIHk9IjAiIGZpbGw9IiMzMzMiIGZvbnQtc2l6ZT0iMTQiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJlbmQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYwLjczNDM3NjkwNzM0ODYzLCA0MjUuMDgzMzMzMzMzMzMzMykgcm90YXRlKDApIj4xMDwvdGV4dD48dGV4dCB4PSIwIiB5PSIwIiBmaWxsPSIjMzMzIiBmb250LXNpemU9IjE0IiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiB0ZXh0LWFuY2hvcj0iZW5kIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MC43MzQzNzY5MDczNDg2MywgNDU5KSByb3RhdGUoMCkiPjA8L3RleHQ+PC9nPjxnIGNsYXNzPSJ0aWNrcyI+PHBhdGggZD0iTSA3MC43MzQzNzY5MDczNDg2Myw1MiBMIDY1LjczNDM3NjkwNzM0ODYzLDUyIiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMiIvPjxwYXRoIGQ9Ik0gNzAuNzM0Mzc2OTA3MzQ4NjMsODUuOTE2NjY2NjY2NjY2NjkgTCA2NS43MzQzNzY5MDczNDg2Myw4NS45MTY2NjY2NjY2NjY2OSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNIDcwLjczNDM3NjkwNzM0ODYzLDExOS44MzMzMzMzMzMzMzMzMSBMIDY1LjczNDM3NjkwNzM0ODYzLDExOS44MzMzMzMzMzMzMzMzMSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNIDcwLjczNDM3NjkwNzM0ODYzLDE1My43NSBMIDY1LjczNDM3NjkwNzM0ODYzLDE1My43NSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNIDcwLjczNDM3NjkwNzM0ODYzLDE4Ny42NjY2NjY2NjY2NjY2OSBMIDY1LjczNDM3NjkwNzM0ODYzLDE4Ny42NjY2NjY2NjY2NjY2OSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNIDcwLjczNDM3NjkwNzM0ODYzLDIyMS41ODMzMzMzMzMzMzMzMSBMIDY1LjczNDM3NjkwNzM0ODYzLDIyMS41ODMzMzMzMzMzMzMzMSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNIDcwLjczNDM3NjkwNzM0ODYzLDI1NS41IEwgNjUuNzM0Mzc2OTA3MzQ4NjMsMjU1LjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTSA3MC43MzQzNzY5MDczNDg2MywyODkuNDE2NjY2NjY2NjY2NjMgTCA2NS43MzQzNzY5MDczNDg2MywyODkuNDE2NjY2NjY2NjY2NjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTSA3MC43MzQzNzY5MDczNDg2MywzMjMuMzMzMzMzMzMzMzMzMzcgTCA2NS43MzQzNzY5MDczNDg2MywzMjMuMzMzMzMzMzMzMzMzMzciIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTSA3MC43MzQzNzY5MDczNDg2MywzNTcuMjUgTCA2NS43MzQzNzY5MDczNDg2MywzNTcuMjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTSA3MC43MzQzNzY5MDczNDg2MywzOTEuMTY2NjY2NjY2NjY2NyBMIDY1LjczNDM3NjkwNzM0ODYzLDM5MS4xNjY2NjY2NjY2NjY3IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMiIvPjxwYXRoIGQ9Ik0gNzAuNzM0Mzc2OTA3MzQ4NjMsNDI1LjA4MzMzMzMzMzMzMzMgTCA2NS43MzQzNzY5MDczNDg2Myw0MjUuMDgzMzMzMzMzMzMzMyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNIDcwLjczNDM3NjkwNzM0ODYzLDQ1OSBMIDY1LjczNDM3NjkwNzM0ODYzLDQ1OSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiLz48L2c+PGcgY2xhc3M9InRpdGxlIj48dGV4dCB4PSIwIiB5PSIwIiBmaWxsPSIjMzMzIiBmb250LXNpemU9IjE2IiBkb21pbmFudC1iYXNlbGluZT0idGV4dC1iZWZvcmUtZWRnZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSwgMjU1LjUpIHJvdGF0ZSgyNzApIj5BdmcgYnVzYncgKEdCL3MpPC90ZXh0PjwvZz48L2c+PC9nPjxnIGNsYXNzPSJtZXJtYWlkLXRtcC1ncm91cCIvPjwvc3ZnPg==" width="760" height="500" class="img_ev3q"></p>
<p>The cross-NUMA <code>1nic-unaligned</code> case (GPU on NUMA 0, NIC on NUMA 1) delivers only <strong>~25 GB/s</strong> -- a <strong>2.2x degradation</strong> compared to the NUMA-aligned <code>1nic-aligned</code> case at <strong>~56 GB/s</strong>, and a <strong>4.5x degradation</strong> compared to the <code>2nic-aligned</code> case at <strong>~112 GB/s</strong>. Three compounding penalties explain this:</p>
<ol>
<li><strong>GDR disabled</strong> -- with a SYS relationship between GPU and NIC, NCCL falls back from <code>GPUDirect</code> to staging through host memory because there is no direct PCIe path</li>
<li><strong>Cross-NUMA</strong> -- every data transfer crosses the QPI/UPI interconnect between NUMA domains</li>
<li><strong>Fewer channels</strong> -- NCCL's topology engine allocates only 2 channels for SYS-distant NICs versus 8 channels with NUMA-aligned NIC (<code>1nic-aligned</code>), and 16 channels with two NUMA-aligned NICs (<code>2nic-aligned</code>)</li>
</ol>
<p>Topology-aware scheduling with GPU-NIC alignment isn't optional for high-performance RDMA. DRANET and the DRA framework give you declarative, fine-grained control over that placement without privileged containers or manual device management.</p>
<p>Connect with the AKS team through our <a href="https://github.com/Azure/AKS/discussions" target="_blank" rel="noopener noreferrer">GitHub discussions</a> or <a href="https://github.com/Azure/AKS/issues" target="_blank" rel="noopener noreferrer">share your feedback and suggestions</a>.</p>]]></content:encoded>
            <category>AI</category>
            <category>GPU</category>
            <category>Networking</category>
            <category>Performance</category>
        </item>
        <item>
            <title><![CDATA[Control where your pods land on AKS with NAP]]></title>
            <link>https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice</link>
            <guid>https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice</guid>
            <pubDate>Mon, 30 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Combine taints, affinity, and topology spread constraints with AKS Node Auto-Provisioning to control where your pods land.]]></description>
            <content:encoded><![CDATA[<p>With Kubernetes, you control what your workloads (or pods) need with resource requests. But how do you control where they land? On AKS, three scheduling levers work with Node Auto-Provisioning (NAP) to give you predictable placement: taints, affinity, and topology spread constraints.</p>
<p><img decoding="async" loading="lazy" alt="Diagram showing NAP topology spread behavior" src="https://blog.aks.azure.com/assets/images/hero-image-318fe5dce2e498de74b1e16d512ea336.png" width="2302" height="514" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>Learn more in the official documentation: <a href="https://learn.microsoft.com/azure/aks/node-auto-provisioning" target="_blank" rel="noopener noreferrer">Node Auto Provisioning</a> and <a href="https://learn.microsoft.com/azure/aks/operator-best-practices-advanced-scheduler" target="_blank" rel="noopener noreferrer">AKS Operator Best Practices</a></p></div></div>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="background">Background<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#background" class="hash-link" aria-label="Direct link to Background" title="Direct link to Background" translate="no">​</a></h2>
<p>You want to ensure that workloads schedule, scale, and are disrupted only when (or where) desired. The problem here is Kubernetes can feel complex, and it's easy to be unclear what settings to use to accomplish this. Node Auto-Provisioning optimizes bin-packing your compute, but to best utilize it - users need to make sure certain best practices are followed for predictable behavior.</p>
<p>When adopting Kubernetes at scale, the hardest operational questions often aren’t “How do I scale nodes (or VMs)?” — they’re:</p>
<ul>
<li>Where will my workload replicas land (zones / nodes)?</li>
<li>How do I express node preferences without accidentally blocking scheduling?</li>
<li>If I’m using Node Auto-Provisioning (NAP), how does it interpret the rules I set?</li>
</ul>
<p>Kubernetes scheduling is a negotiation between:</p>
<ul>
<li>Workload intent (what your pod spec asks for)</li>
<li>Available capacity (what nodes exist, and what the platform can create)</li>
</ul>
<p>This workload intent can be expressed in your workload manifest using 3 levers:</p>
<ol>
<li><strong>Taints and Tolerations</strong> – control which pods can go to which nodes</li>
<li><strong>Affinity/Anti-Affinity</strong> – control where workloads can (or should not) run</li>
<li><strong>Topology Spread Constraints</strong> – control replica distribution across failure domains</li>
</ol>
<p>This post will connect NAP with three most important workload-level tools for shaping predictable node provisioning outcomes on AKS. Then we’ll connect the dots to explain what AKS Node Auto-Provisioning (NAP) does with those signals to manage your workloads.</p>
<p>If you’re new to these Kubernetes features, this post will give you “good defaults” as a starting point. If you’re already deep into scheduling, treat it as a checklist for the behaviors AKS users most commonly ask about.</p>
<p>For more on operator and scheduling best practices, visit <a href="https://learn.microsoft.com/azure/aks/operator-best-practices-advanced-scheduler" target="_blank" rel="noopener noreferrer">operator best-practices guidance</a> or configure the AKS scheduler using <a href="https://learn.microsoft.com/azure/aks/configure-aks-scheduler?tabs=new-cluster" target="_blank" rel="noopener noreferrer">Configurable Scheduler Profiles</a>. Watch for an upcoming post that explains how to align pod placement to achieve cost efficiencies with Configurable Scheduler Profiles on AKS.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-nap-handles-node-selection">How NAP handles node selection<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#how-nap-handles-node-selection" class="hash-link" aria-label="Direct link to How NAP handles node selection" title="Direct link to How NAP handles node selection" translate="no">​</a></h2>
<p>Node auto-provisioning provisions, scales, and manages nodes. NAP senses pending pod pressure, provisions new nodes that satisfy workload specs and NodePool allowed options — and then AKS schedules pods onto those nodes.</p>
<p>NAP uses the following levers to control workload scheduling:</p>
<ul>
<li><a href="https://learn.microsoft.com/azure/aks/node-auto-provisioning-node-pools" target="_blank" rel="noopener noreferrer">NodePool CRD</a> (policies / constraints) - Node settings like (SKU selection, capacity type, zones, labels, node-level resource limits)</li>
<li><a href="https://learn.microsoft.com/azure/aks/node-auto-provisioning-aksnodeclass" target="_blank" rel="noopener noreferrer">AKSNodeClass CRD</a> (policies / constraints) - Azure-specific node settings like subnet behavior, image/OS disk/kubelet configuration, etc</li>
<li>NodeClaims - detail the state of provisioned and provisioning nodes</li>
<li>Workload spec / deployment file - The Kubernetes manifest that defines your workload's resource requirements and scheduling constraints (Node Affinity, Tolerations, and Topology Spread Constraints)</li>
</ul>
<p>Simply put, NAP uses each lever to express “where and how this pod should run”, “what nodes should exist for this class of workloads”, and track what nodes are being scheduled or currently running.</p>
<p>You can think of the NodePool/AKSNodeClass as your "acceptable range of compute options” which your workload intent has to fit inside it. Check out our documentation for a good default spec for your <a href="https://learn.microsoft.com/azure/aks/node-auto-provisioning-node-pools#review-default-node-pool-configuration" target="_blank" rel="noopener noreferrer">NodePool CRD</a> and the options for your <a href="https://learn.microsoft.com/azure/aks/node-auto-provisioning-aksnodeclass#comprehensive-aksnodeclass-configuration-example" target="_blank" rel="noopener noreferrer">AKSNodeClass CRD</a> configuration.</p>
<p><em><strong>Note:</strong></em> NAP is a node-level (or infrastructure) autoscaler that provisions and manages nodes (VMs) based on pending pods and their requirements, while the Kubernetes scheduler continues to schedule pods onto those nodes. For application level autoscaling, you can use <a href="https://learn.microsoft.com/azure/aks/keda-about" target="_blank" rel="noopener noreferrer">KEDA</a> with NAP. We also suggest using <a href="https://learn.microsoft.com/azure/aks/vertical-pod-autoscaler" target="_blank" rel="noopener noreferrer">Vertical Pod Autoscaler (VPA)</a> in recommendation-only mode (for example, with <code>updateMode: Off</code> in the VPA custom resource) for resource sizing recommendations.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="part-1--topology-spread-constraints-tool-for-zone-aware-replicas">Part 1 — Topology Spread Constraints: tool for zone-aware replicas<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#part-1--topology-spread-constraints-tool-for-zone-aware-replicas" class="hash-link" aria-label="Direct link to Part 1 — Topology Spread Constraints: tool for zone-aware replicas" title="Direct link to Part 1 — Topology Spread Constraints: tool for zone-aware replicas" translate="no">​</a></h2>
<p><strong>Topology Spread Constraints</strong> let you tell the scheduler: “Keep these replicas balanced across domains like zones or nodes.” The Kubernetes documentation describes them as a way to spread pods across failure domains such as regions, zones, nodes, and custom topology keys.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="how-nap-handles-topology-spread">How NAP handles Topology Spread<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#how-nap-handles-topology-spread" class="hash-link" aria-label="Direct link to How NAP handles Topology Spread" title="Direct link to How NAP handles Topology Spread" translate="no">​</a></h3>
<p>NAP honors workload <a href="https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/#topologyspreadconstraints-field" target="_blank" rel="noopener noreferrer">topologySpreadConstraints</a>. While you can list the allowed zones in the NodePool CRD, <code>topologySpreadConstraints</code> are the means to ensure topology spread.</p>
<ul>
<li>NAP (<strong>without</strong> pod-level <code>topologySpreadConstraints</code> defined) will provision wherever there is availability for the preferred VM SKU. This can look like NAP provisioning all preferred nodes in zone 1 and none in zone 2 and zone 3.</li>
<li>NAP (<strong>with</strong> pod-level <code>topologySpreadConstraints</code> defined) ensures topology spread. NAP honors pod-level constraints (number of replicas, topology spread behavior) in the workload deployment file. See the Kubernetes docs on topology spread for other examples also.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="a-good-default-spread-across-availability-zones">A good default: spread across Availability Zones<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#a-good-default-spread-across-availability-zones" class="hash-link" aria-label="Direct link to A good default: spread across Availability Zones" title="Direct link to A good default: spread across Availability Zones" translate="no">​</a></h3>
<p>Here’s a typical “3-zone spread” pattern for a Deployment:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> web</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">topologySpreadConstraints</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">maxSkew</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">minDomains</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">topologyKey</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> topology.kubernetes.io/zone</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">whenUnsatisfiable</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> DoNotSchedule</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> web</span><br></span></code></pre></div></div>
<p>What these fields mean (in plain language):</p>
<ul>
<li>topologyKey: topology.kubernetes.io/zone → spread across zones (not just nodes).</li>
<li>maxSkew: 1 → keep zone counts close (difference between most/least loaded domains can’t exceed 1 when DoNotSchedule).</li>
<li>minDomains: 3 (only valid with DoNotSchedule) → treat it as a requirement that at least 3 eligible domains participate; if fewer than minDomains are eligible, Kubernetes treats the “global minimum” as 0, affecting skew calculation.</li>
<li>whenUnsatisfiable: DoNotSchedule → enforce the rule strictly; if it can’t be met, pods stay Pending.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="hard-vs-soft-topology-spreading">“Hard” vs “soft” topology spreading<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#hard-vs-soft-topology-spreading" class="hash-link" aria-label="Direct link to “Hard” vs “soft” topology spreading" title="Direct link to “Hard” vs “soft” topology spreading" translate="no">​</a></h3>
<p>Kubernetes gives you two behaviors:</p>
<ul>
<li><em>DoNotSchedule</em>: strict; better for HA-critical workloads, but can stall rollouts (pods stay pending) if capacity is constrained.</li>
<li><em>ScheduleAnyway</em>: best-effort; scheduler still places pods wherever there is capacity but prioritizes choices that reduce skew.</li>
</ul>
<p>The following example uses the "soft rule" of <code>whenUnsatisfiable: ScheduleAnyway</code> which will attempt to spread workloads across zones evenly, but will prioritize scale-up success over even topology spread. This method does not guarantee topology spread, so consider tradeoffs between zonal resiliency and scheduling success.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> web</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">topologySpreadConstraints</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">maxSkew</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">topologyKey</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> topology.kubernetes.io/zone</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">whenUnsatisfiable</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ScheduleAnyway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> web</span><br></span></code></pre></div></div>
<p><strong>Practical guidance:</strong></p>
<p>Start with <code>DoNotSchedule</code> for Tier-0 services where zonal placement is critical and more important than scheduling speed.
Use <code>ScheduleAnyway</code> if you’d rather progress than block workload readiness during partial zone pressure.</p>
<p>For more info, visit the <a href="https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/#topologyspreadconstraints-field" target="_blank" rel="noopener noreferrer">upstream Kubernetes docs on topology spread constraints</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="part-2--node-affinity--anti-affinity-shaping-which-nodes-are-eligible">Part 2 — Node Affinity / Anti-Affinity: shaping which nodes are eligible<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#part-2--node-affinity--anti-affinity-shaping-which-nodes-are-eligible" class="hash-link" aria-label="Direct link to Part 2 — Node Affinity / Anti-Affinity: shaping which nodes are eligible" title="Direct link to Part 2 — Node Affinity / Anti-Affinity: shaping which nodes are eligible" translate="no">​</a></h2>
<p>Node affinity is the evolution of <a href="https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector" target="_blank" rel="noopener noreferrer">nodeSelector</a>: it’s more expressive and lets you define hard requirements vs soft preferences.</p>
<p>Common use cases:</p>
<p>Standard Example (with nodeAffinity) - sets a hard rule using <code>requiredDuringSchedulingIgnoredDuringExecution</code> requiring gpu support:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">affinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">nodeAffinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">requiredDuringSchedulingIgnoredDuringExecution</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">nodeSelectorTerms</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">matchExpressions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> accelerator</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">operator</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> In</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">values</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> gpu</span><br></span></code></pre></div></div>
<p>Standard Example (with nodeAffinity) - “Prefer this node type, but don’t block if it’s unavailable” - Uses a soft rule of <code>preferredDuringSchedulingIgnoredDuringExecution</code> that prefer a specific SKU, but will apply best effort and schedule elsewhere if this SKU is unavailable:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">affinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">nodeAffinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">preferredDuringSchedulingIgnoredDuringExecution</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">weight</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">preference</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">matchExpressions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> node.kubernetes.io/instance</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">type</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">operator</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> In</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">values</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> Standard_D16ds_v5</span><br></span></code></pre></div></div>
<p>Standard Example - “Never co-locate replicas on the same node”</p>
<p>That’s usually <code>podAntiAffinity</code> or topology spread across hostname.
This scenario uses a hard rule <code>DoNotSchedule</code> to spread pods using kubernetes.io/hostname:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">topologySpreadConstraints</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">maxSkew</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">topologyKey</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kubernetes.io/hostname</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">whenUnsatisfiable</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> DoNotSchedule</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> web</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="node-affinity-example---gaming-workload">Node Affinity Example - Gaming Workload<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#node-affinity-example---gaming-workload" class="hash-link" aria-label="Direct link to Node Affinity Example - Gaming Workload" title="Direct link to Node Affinity Example - Gaming Workload" translate="no">​</a></h3>
<p>The following example workload uses best effort topology spread with a hard rule node affinity.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sample</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gaming</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">low</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">latency</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">prefer</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">local</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">12</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sample</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gaming</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">low</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">latency</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">prefer</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">local</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sample</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gaming</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">low</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">latency</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">prefer</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">local</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">affinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">nodeAffinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">preferredDuringSchedulingIgnoredDuringExecution</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">weight</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">preference</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">matchExpressions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> example.com/network</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">tier</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token key atrule" style="color:#00a4db">operator</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> In</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token key atrule" style="color:#00a4db">values</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"low-latency"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">topologySpreadConstraints</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">maxSkew</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">topologyKey</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> topology.kubernetes.io/zone</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">whenUnsatisfiable</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ScheduleAnyway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sample</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gaming</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">low</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">latency</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">prefer</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">local</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> session</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mcr.microsoft.com/oss/kubernetes/pause</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">3.6</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"500m"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"256Mi"</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="how-do-topology-spread-constraints-interact-with-node-affinity-rules">How do Topology Spread Constraints interact with Node Affinity rules?<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#how-do-topology-spread-constraints-interact-with-node-affinity-rules" class="hash-link" aria-label="Direct link to How do Topology Spread Constraints interact with Node Affinity rules?" title="Direct link to How do Topology Spread Constraints interact with Node Affinity rules?" translate="no">​</a></h3>
<p>Both Topology Spread Constraints and Node Affinity have hard and soft controls. If you set both, depending on how you configure them, Kubernetes and AKS will factor them into scheduling logic in multiple ways.</p>
<p>Node Affinity rules can either be:</p>
<ul>
<li>Hard Rule - <code>requiredDuringSchedulingIgnoredDuringExecution</code></li>
<li>Soft Rule (best effort) - <code>preferredDuringSchedulingIgnoredDuringExecution</code></li>
</ul>
<p>Topology Spread Constraint can either be:</p>
<ul>
<li>Hard Rule - <code>whenUnsatisfiable: DoNotSchedule</code></li>
<li>Soft Rule (best effort) - <code>whenUnsatisfiable: ScheduleAnyway</code></li>
</ul>
<p>The following table lists what to expect when you set these two constraints together in common scenarios, and our recommended setting:</p>
<table><thead><tr><th>Topology Spread Configuration</th><th>Affinity Configuration</th><th>Observed Scheduling Behavior</th><th>Recommendation</th></tr></thead><tbody><tr><td><strong>Hard</strong> (<code>whenUnsatisfiable: DoNotSchedule</code>)</td><td><strong>Hard Node Affinity</strong> (<code>requiredDuringSchedulingIgnoredDuringExecution</code>)</td><td>Pod remains <strong>Pending</strong> if no node satisfies both constraints. The scheduler filters out all nodes that violate either rule.</td><td>Use only when you are certain the constraints are always compatible (for example, multi‑zone node affinity plus multi‑zone spread). Avoid mixing single‑zone affinity with multi‑zone spread.</td></tr><tr><td><strong>Soft</strong> (<code>whenUnsatisfiable: ScheduleAnyway</code>)</td><td><strong>Hard Node Affinity</strong> (<code>requiredDuringSchedulingIgnoredDuringExecution</code>)</td><td>Pod schedules only on nodes matching affinity. Topology spread is applied as <strong>best‑effort</strong>, and distribution may be uneven.</td><td>✅ <strong>Recommended default</strong> for most workloads. Enforce strict placement requirements while keeping high availability best‑effort.</td></tr><tr><td><strong>Hard</strong> (<code>whenUnsatisfiable: DoNotSchedule</code>)</td><td><strong>Soft Node Affinity</strong> (<code>preferredDuringSchedulingIgnoredDuringExecution</code>)</td><td>Pod schedules only if topology spread constraints are met. Affinity acts only as a preference among valid nodes.</td><td>Use when even distribution across zones or nodes is more important than node‑level preferences.</td></tr><tr><td><strong>Soft</strong> (<code>whenUnsatisfiable: ScheduleAnyway</code>)</td><td><strong>Soft Node Affinity</strong></td><td>Pod always schedules. Both constraints only influence scoring; placement is flexible and may be imbalanced.</td><td>Suitable for dev/test, batch, or low‑criticality workloads.</td></tr><tr><td><strong>Hard multi‑zone spread</strong> (<code>whenUnsatisfiable: DoNotSchedule</code> and <code>minDomains</code> &gt;= 2)</td><td><strong>Single‑zone hard affinity</strong></td><td>Pod enters a permanent <strong>Pending</strong> state due to a logical contradiction between constraints.</td><td>Align affinity and spread to the same topology domains, or relax one of the constraints.</td></tr></tbody></table>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="practical-guidance">Practical Guidance<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#practical-guidance" class="hash-link" aria-label="Direct link to Practical Guidance" title="Direct link to Practical Guidance" translate="no">​</a></h4>
<p>Consider your priorities for topology spread and node affinity.</p>
<ul>
<li>Decide which requirement is truly “must-have.”<!-- -->
<ul>
<li>Make that one hard (required… or DoNotSchedule).</li>
<li>Make the other a preference (preferred… or ScheduleAnyway).</li>
</ul>
</li>
<li>If you combine strict affinity with strict multi-zone spread, double-check feasibility:<!-- -->
<ul>
<li>If affinity restricts you to 1 zone, you cannot also require even spread across 3 zones with <code>DoNotSchedule</code>.</li>
</ul>
</li>
<li>Use <code>nodeAffinityPolicy: Honor</code> when your intent is “spread within the nodes I’ve made eligible via affinity.”</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="part-3---taints-and-tolerations">Part 3 - Taints and tolerations<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#part-3---taints-and-tolerations" class="hash-link" aria-label="Direct link to Part 3 - Taints and tolerations" title="Direct link to Part 3 - Taints and tolerations" translate="no">​</a></h2>
<p>Taints and tolerations control which pods can run on which nodes. Think of a taint as a "keep out" sign on a node. If your pod doesn't carry a matching toleration, the scheduler skips that node.</p>
<p>Taints and tolerations are mechanisms used in Kubernetes to control which pods can be scheduled onto which nodes. They allow you to ensure that certain pods do not run on particular nodes, enabling more fine-grained control over your clusters.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="a-practical-aksnap-mental-model-for-taints-and-tolerations">A Practical AKS/NAP Mental Model for taints and tolerations<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#a-practical-aksnap-mental-model-for-taints-and-tolerations" class="hash-link" aria-label="Direct link to A Practical AKS/NAP Mental Model for taints and tolerations" title="Direct link to A Practical AKS/NAP Mental Model for taints and tolerations" translate="no">​</a></h3>
<p>Think of taints as a ‘Do Not Enter’ sign on a node. If a node has a specific taint, pods must tolerate it to be scheduled on that node. This helps families of workloads maintain their operational boundaries while ensuring they run on appropriate resources.</p>
<p>Taints are defined in your <a href="https://learn.microsoft.com/azure/aks/node-auto-provisioning-node-pools" target="_blank" rel="noopener noreferrer">NodePool CRD</a>, and Tolerations are defined in your workload deployment file.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="taint-your-nap-managed-nodes">Taint your NAP-managed nodes<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#taint-your-nap-managed-nodes" class="hash-link" aria-label="Direct link to Taint your NAP-managed nodes" title="Direct link to Taint your NAP-managed nodes" translate="no">​</a></h3>
<p>In NAP, you can provide taints in your <a href="https://learn.microsoft.com/azure/aks/node-auto-provisioning-node-pools" target="_blank" rel="noopener noreferrer">NodePool CRD</a>, and every node created based on this NodePool CRD will have this taint. If you only want specific nodes to have this taint, make sure you have a specific NodePool CRD file created for this purpose (as you can have multiple NodePool CRDs).</p>
<p>The following example shows a taint called <code>test.com/custom-taint</code> that is added in the <code>spec.template.spec.taints</code> field in a <a href="https://learn.microsoft.com/azure/aks/node-auto-provisioning-node-pools" target="_blank" rel="noopener noreferrer">NodePool CRD</a>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> karpenter.sh/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> NodePool</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> default</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">taints</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> test.com/custom</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">taint</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">effect</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> NoSchedule</span><br></span></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: Taints can prevent pods from being scheduled to these nodes if they are not tolerated by the pods. A proper toleration must be added to your specific pods to allow them to be scheduled to nodes that are based on this NodePool CRD.</p>
</blockquote>
<p>Tolerations are a field in your pod spec that declare which taint effects a pod can accept. The two most common taint effects are:</p>
<ul>
<li><code>NoSchedule</code>: strict. Only pods with a matching toleration can land on the tainted node.</li>
<li><code>PreferNoSchedule</code>: best-effort. AKS tries to avoid placing pods that don't tolerate the taint, but doesn't guarantee it.</li>
</ul>
<p>Hard Rule - <strong>NoSchedule Toleration</strong> example:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token key atrule" style="color:#00a4db">tolerations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"key1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token key atrule" style="color:#00a4db">operator</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Equal"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token key atrule" style="color:#00a4db">value</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"value1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token key atrule" style="color:#00a4db">effect</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"NoSchedule"</span><br></span></code></pre></div></div>
<p>Best-effort rule - <strong>PreferNoSchedule Toleration</strong> example:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">tolerations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"key2"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">operator</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Equal"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">value</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"value2"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">effect</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"PreferNoSchedule"</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="healthcare-workload-example">Healthcare workload example<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#healthcare-workload-example" class="hash-link" aria-label="Direct link to Healthcare workload example" title="Direct link to Healthcare workload example" translate="no">​</a></h3>
<p>The following example workload uses hard rule topology spread with a hard rule node affinity and toleration to ensure resiliency and limited node co-location.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sample</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">healthcare</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">zone</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">hardened</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sample</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">healthcare</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">zone</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">hardened</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sample</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">healthcare</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">zone</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">hardened</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># If you taint compliance nodes like: example.com/compliance=phi:NoSchedule</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">tolerations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"example.com/compliance"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">operator</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Equal"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">value</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"phi"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">effect</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"NoSchedule"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">affinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">podAntiAffinity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># HARD anti-affinity at hostname: don't put 2 replicas on same node.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># Tradeoff: can go Pending if cluster is small.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">requiredDuringSchedulingIgnoredDuringExecution</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sample</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">healthcare</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">zone</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">hardened</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">topologyKey</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kubernetes.io/hostname</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">topologySpreadConstraints</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">maxSkew</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">minDomains</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">topologyKey</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> topology.kubernetes.io/zone</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">whenUnsatisfiable</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> DoNotSchedule  </span><span class="token comment" style="color:#999988;font-style:italic"># HARD: ensure zone spread for HA</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">labelSelector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sample</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">healthcare</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">phi</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">zone</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">hardened</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> api</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mcr.microsoft.com/oss/kubernetes/pause</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">3.6</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"250m"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"256Mi"</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="common-taint--toleration-pitfalls">Common Taint + Toleration Pitfalls<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#common-taint--toleration-pitfalls" class="hash-link" aria-label="Direct link to Common Taint + Toleration Pitfalls" title="Direct link to Common Taint + Toleration Pitfalls" translate="no">​</a></h3>
<ul>
<li>Over-tainting Nodes: Be cautious not to overuse taints as they can create scheduling issues.</li>
<li>Complexity in Management: Managing multiple taints and tolerations can become complex, making debugging and management harder.</li>
</ul>
<p>For more on Taints and Tolerations, visit our <a href="https://learn.microsoft.com/azure/aks/operator-best-practices-advanced-scheduler#provide-dedicated-nodes-using-taints-and-tolerations" target="_blank" rel="noopener noreferrer">operator best practices docs</a> or the <a href="https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/" target="_blank" rel="noopener noreferrer">Kubernetes documentation</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="recap">Recap<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#recap" class="hash-link" aria-label="Direct link to Recap" title="Direct link to Recap" translate="no">​</a></h2>
<p>General Recommendations:</p>
<ul>
<li>Configure your NAP NodePool CRD spec to allow as flexible (or as specific) of a range of nodes that your workloads are allowed to schedule to. You can also create multiple NodePool CRDs, just make sure they are mutually exclusive.</li>
<li>Configure your AKSNodeClass CRD spec to define any Azure-specific settings.</li>
<li>Define desired scheduling behavior in the deployment spec with Topology Spread Constraints, Node Affinity, and/or Taints and Tolerations.</li>
<li>Consider your priorities between Topology Spread and certain Node Affinities, and set one with a hard rule and the other with best effort.</li>
<li>Use Taints and Tolerations to ensure special workloads schedule only to the exact nodes you want them to. Be sure to use in moderation to not overcomplicate your cluster.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="faq">FAQ<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#faq" class="hash-link" aria-label="Direct link to FAQ" title="Direct link to FAQ" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="how-can-i-overprovision-to-respond-to-spikes-of-traffic">How can I overprovision to respond to spikes of traffic?<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#how-can-i-overprovision-to-respond-to-spikes-of-traffic" class="hash-link" aria-label="Direct link to How can I overprovision to respond to spikes of traffic?" title="Direct link to How can I overprovision to respond to spikes of traffic?" translate="no">​</a></h3>
<p>When using NAP, you can set your resource needs slightly higher than you expect to actually use. NAP responds to pending pod pressure, so by default it provisions nodes to match the amount you request in your deployment file. When not using an autoscaler, you have the option to use <a href="https://learn.microsoft.com/azure/aks/best-practices-performance-scale#overprovisioning" target="_blank" rel="noopener noreferrer">overprovisioning</a> to have excess compute to respond quickly to spikes of traffic.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="how-can-i-reduce-latency-when-trying-to-schedule-nodes">How can I reduce latency when trying to schedule nodes?<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#how-can-i-reduce-latency-when-trying-to-schedule-nodes" class="hash-link" aria-label="Direct link to How can I reduce latency when trying to schedule nodes?" title="Direct link to How can I reduce latency when trying to schedule nodes?" translate="no">​</a></h3>
<p>You can consider enabling features such as <a href="https://learn.microsoft.com/azure/aks/artifact-streaming" target="_blank" rel="noopener noreferrer">Artifact Streaming</a> which can decrease pod readiness time.</p>
<p>For more visit our documentation on <a href="https://learn.microsoft.com/azure/aks/best-practices-performance-scale" target="_blank" rel="noopener noreferrer">performance and scaling best practices</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="next-steps">Next steps<a href="https://blog.aks.azure.com/2026/03/20/node-provisioning-best-practice#next-steps" class="hash-link" aria-label="Direct link to Next steps" title="Direct link to Next steps" translate="no">​</a></h2>
<p>Ready to get started?</p>
<ol>
<li><strong>Try NAP today:</strong> Follow the <a href="https://learn.microsoft.com/azure/aks/use-node-auto-provisioning" target="_blank" rel="noopener noreferrer">Enable Node Auto Provisioning steps</a>.</li>
<li><strong>Learn more:</strong> Visit our AKS <a href="https://learn.microsoft.com/azure/aks/operator-best-practices-advanced-scheduler" target="_blank" rel="noopener noreferrer">operator best-practices guidance</a>.</li>
<li><strong>Share feedback:</strong> Open issues or ideas in <a href="https://github.com/Azure/AKS/issues" target="_blank" rel="noopener noreferrer">AKS GitHub Issues</a>.</li>
<li><strong>Join the community:</strong> Subscribe to the <a href="https://www.youtube.com/@theakscommunity" target="_blank" rel="noopener noreferrer">AKS Community YouTube</a> and follow <a href="https://x.com/theakscommunity" target="_blank" rel="noopener noreferrer">@theakscommunity</a> on X.</li>
</ol>]]></content:encoded>
            <category>Node Auto Provisioning</category>
            <category>Scheduler</category>
        </item>
        <item>
            <title><![CDATA[AKS Control Plane Enhancements]]></title>
            <link>https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements</link>
            <guid>https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements</guid>
            <pubDate>Mon, 30 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how AKS improves control plane scalability and stability through a new set of control plane enhancements]]></description>
            <content:encoded><![CDATA[<p>Azure Kubernetes Service (AKS) now includes several control plane enhancements to enable large clusters scale more efficiently and operate reliably. These enhancements include streaming LIST responses, higher control plane resource limits, API server guard and etcd defragmentation optimizations.</p>
<p><img decoding="async" loading="lazy" alt="AKS control plane enhancements for scalability, stability and performance" src="https://blog.aks.azure.com/assets/images/control-plane-enhancements-large-1aa466602f55f165bc80b7daedf0fc6c.png" width="800" height="532" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>A key factor in Kubernetes scalability is how clients interact with the control plane and how those interactions shape the cluster’s overall scale envelope. Unoptimized API server call patterns by clients and growing etcd footprints can place increasing pressure on the control plane, ultimately limiting cluster scalability. To address this, AKS includes a set of built‑in control plane enhancements for large clusters that automatically improve scalability, performance, and stability without requiring any manual configuration from customers.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="streaming-encoder-for-list-responses">Streaming encoder for LIST responses<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#streaming-encoder-for-list-responses" class="hash-link" aria-label="Direct link to Streaming encoder for LIST responses" title="Direct link to Streaming encoder for LIST responses" translate="no">​</a></h2>
<p>The API server's response encoders traditionally serialize the entire response into a contiguous block of memory and perform one <a href="https://pkg.go.dev/net/http#ResponseWriter.Write" target="_blank" rel="noopener noreferrer">ResponseWriter.Write</a> call to transmit data to the client. If multiple large LIST requests arrive simultaneously, the cumulative memory consumption can grow quickly, leading to Out-of-Memory (OOM) events that compromise cluster stability.</p>
<p>Kubernetes v1.33 introduced <a href="https://kubernetes.io/blog/2025/05/09/kubernetes-v1-33-streaming-list-responses/" target="_blank" rel="noopener noreferrer">streaming encoding for LIST responses</a>. This approach processes and transmits each item individually, so memory is freed progressively as each chunk is sent. In benchmarks, this reduced memory usage by up to 20x in heavy LIST scenarios.</p>
<p>This capability is backported to AKS versions 1.31.9+ and 1.32.6+, so your clusters benefit before upgrading to 1.33.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="benefits">Benefits<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#benefits" class="hash-link" aria-label="Direct link to Benefits" title="Direct link to Benefits" translate="no">​</a></h3>
<ul>
<li><strong>Reduced memory consumption</strong>: Your API server uses significantly less memory when handling large list requests. This reduces the likelihood of OOM events, thereby improving API server response time.</li>
<li><strong>Increased scalability and stability</strong>: Your API server can handle more concurrent requests and larger datasets, increasing your cluster's current scale ceiling.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="higher-control-plane-resource-limits">Higher control plane resource limits<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#higher-control-plane-resource-limits" class="hash-link" aria-label="Direct link to Higher control plane resource limits" title="Direct link to Higher control plane resource limits" translate="no">​</a></h2>
<p>AKS autoscales your control plane based on cluster size, measured by total compute cores in the cluster, and the control plane's CPU and memory utilization. With this enhancement, your AKS control plane can now receive up to 4x higher CPU and memory limits during scaling. This gives large clusters more room to handle the most demanding workloads.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="benefits-1">Benefits<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#benefits-1" class="hash-link" aria-label="Direct link to Benefits" title="Direct link to Benefits" translate="no">​</a></h3>
<ul>
<li><strong>Greater scalability</strong>: Your cluster can support more nodes and workloads. This is especially beneficial for advanced scenarios such as AI inference and training.</li>
<li><strong>Lower latency</strong>: Higher CPU and memory help reduce your API server's response time.</li>
<li><strong>Higher stability</strong>: Your control plane encounters fewer bottlenecks and remains more stable under heavy load.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="aks-managed-api-server-guard">AKS managed API server guard<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#aks-managed-api-server-guard" class="hash-link" aria-label="Direct link to AKS managed API server guard" title="Direct link to AKS managed API server guard" translate="no">​</a></h2>
<p>When the API server remains unstable after scaling to the maximum control plane resource limits, and out-of-memory (OOM) incidents continue, AKS applies a <a href="https://learn.microsoft.com/troubleshoot/azure/azure-kubernetes/create-upgrade-delete/troubleshoot-apiserver-etcd?tabs=resource-specific#cause-4-aks-managed-api-server-guard-was-applied" target="_blank" rel="noopener noreferrer">managed flow schema and priority level configuration</a> that throttles non-system API server requests.</p>
<p>In most cases, resource-intensive LIST operations from unoptimized clients trigger this instability. This last-resort safeguard keeps the API server stable and operational, even under extreme load.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="benefits-2">Benefits<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#benefits-2" class="hash-link" aria-label="Direct link to Benefits" title="Direct link to Benefits" translate="no">​</a></h3>
<ul>
<li><strong>Protects API server integrity</strong>: Prevents your API server from becoming unresponsive due to excessive load, helping preserve overall cluster stability.</li>
<li><strong>Simplified troubleshooting</strong>: AKS proactively notifies you through a <a href="https://learn.microsoft.com/azure/service-health/resource-health-overview" target="_blank" rel="noopener noreferrer">resource health notification</a> when API server guard is applied. The <a href="https://learn.microsoft.com/troubleshoot/azure/azure-kubernetes/create-upgrade-delete/troubleshoot-apiserver-etcd?tabs=resource-specific#step-2---identify-and-analyse-latency-for-user-agent" target="_blank" rel="noopener noreferrer">API server resource intensive listing detector</a> in Diagnose &amp; Solve helps you identify unoptimized clients. Once client call patterns are optimized, you also have the ability to override or modify the managed API server guard.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Resource Health alert notification indicating that AKS managed API server guard was applied to your cluster" src="https://blog.aks.azure.com/assets/images/rh-apf-image-975d3b5f0d261d7105f9f244a65fd17b.png" width="1024" height="489" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="etcd-defragmentation-optimizations">etcd defragmentation optimizations<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#etcd-defragmentation-optimizations" class="hash-link" aria-label="Direct link to etcd defragmentation optimizations" title="Direct link to etcd defragmentation optimizations" translate="no">​</a></h2>
<p>Defragmentation is essential for reclaiming unused space in etcd by rewriting fragmented data into contiguous storage. Because defragmentation blocks reads and writes, it runs on one etcd replica at a time, and larger databases take longer to complete.</p>
<p>AKS now includes etcd defragmentation optimizations for large clusters, reducing defragmentation time by up to 50%. For example, in a sample cluster with an etcd size of about 2 GB, per-replica defragmentation time decreased from about 18 seconds to about 9 seconds.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="benefits-3">Benefits<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#benefits-3" class="hash-link" aria-label="Direct link to Benefits" title="Direct link to Benefits" translate="no">​</a></h3>
<ul>
<li>Reduces your API server's response time spikes and transient client timeouts during etcd operations that serve client reads and writes.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://blog.aks.azure.com/2026/03/30/aks-control-plane-enhancements#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>These improvements make your control plane more resilient, scalable, and performant, and reduce the manual configuration needed to scale your existing clusters to handle the most demanding workloads. Always remember, the Kubernetes scale envelope remains multidimensional. The number and size of cluster objects, such as pods, nodes, CRDs, Secrets, ConfigMaps, and other resources along with client behavior, continue to play a critical role in how efficiently your cluster scales.</p>
<p>We’re always working to improve the Kubernetes control plane upstream and the AKS control plane downstream to make the platform more reliable and easier to operate. If you have feedback or ideas, we’d love to hear from you.</p>
<p>To learn more about the Kubernetes scale envelope, its interaction with the control plane, client optimization, creating resource health alerts and best practices for running large clusters, refer to:</p>
<ul>
<li><strong><a href="https://learn.microsoft.com/azure/aks/best-practices-performance-scale-large" target="_blank" rel="noopener noreferrer">AKS Best Practices for Large Clusters</a></strong></li>
<li><strong><a href="https://learn.microsoft.com/troubleshoot/azure/azure-kubernetes/create-upgrade-delete/troubleshoot-apiserver-etcd" target="_blank" rel="noopener noreferrer">API Server and etcd - Troubleshooting Guide</a></strong></li>
<li><strong><a href="https://learn.microsoft.com/azure/service-health/resource-health-alert-arm-template-guide" target="_blank" rel="noopener noreferrer">Create Resource Health Alerts</a></strong></li>
</ul>]]></content:encoded>
            <category>Operations</category>
            <category>Scaling</category>
            <category>Performance</category>
        </item>
        <item>
            <title><![CDATA[Announcing Gateway API support for App Routing]]></title>
            <link>https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api</link>
            <guid>https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api</guid>
            <pubDate>Wed, 18 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The AKS app routing add-on now supports the Kubernetes Gateway API via a meshless Istio control plane — the recommended path to migrate from Ingress-NGINX.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://gateway-api.sigs.k8s.io/images/logo/logo-text-horizontal.png" alt="Gateway API logo" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Update — now generally available</div><div class="admonitionContent_BuS1"><p>As of <strong>AKS release v20260428 (April 28, 2026)</strong>, both <strong>managed Gateway API</strong> (<code>--enable-gateway-api</code>) and the <strong>app routing Istio</strong> Gateway API implementation (<code>--enable-app-routing-istio</code>) are <strong>generally available</strong> on AKS.</p><p>You no longer need preview feature flags or the <code>aks-preview</code> extension; the steps later in this post reflect the original preview experience.</p></div></div>
<p>We're announcing preview support for the <strong>Kubernetes Gateway API</strong> in the AKS application routing add-on. This brings a modern, role-oriented traffic management model to AKS — and establishes a clear migration path ahead of the <a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#why-now-the-ingress-nginx-retirement">upcoming Ingress-NGINX retirement</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="background-the-kubernetes-networking-stack-is-evolving">Background: the Kubernetes networking stack is evolving<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#background-the-kubernetes-networking-stack-is-evolving" class="hash-link" aria-label="Direct link to Background: the Kubernetes networking stack is evolving" title="Direct link to Background: the Kubernetes networking stack is evolving" translate="no">​</a></h2>
<p>For years, the Kubernetes <a href="https://kubernetes.io/docs/concepts/services-networking/ingress/" target="_blank" rel="noopener noreferrer">Ingress API</a> has been the standard way to expose HTTP services running in a cluster. It works, but it has real limitations: the spec is intentionally minimal, leaving providers to implement advanced routing via non-standard annotations, and its flat, single-resource model doesn't map well to real-world organizational boundaries where platform teams and application teams have different concerns.</p>
<p>The <a href="https://gateway-api.sigs.k8s.io/" target="_blank" rel="noopener noreferrer">Kubernetes Gateway API</a> was designed by SIG Network to address these gaps. It introduces a layered, role-oriented model:</p>
<ul>
<li><strong>GatewayClass</strong> — defines the type of gateway infrastructure (managed by infrastructure operators)</li>
<li><strong>Gateway</strong> — instantiates a gateway and its listeners (managed by cluster operators)</li>
<li><strong>HTTPRoute / GRPCRoute / etc.</strong> — bind application traffic rules to a gateway (managed by application developers)</li>
</ul>
<p>This separation means a platform team can provision and own shared gateway infrastructure, while application teams independently control their routing rules — without needing elevated access or custom annotations. Gateway API also has first-class support for features like traffic splitting, header-based routing, and backend weighting that previously required vendor-specific workarounds.</p>
<p>Gateway API is the direction Kubernetes networking is heading. The Ingress API is not going away, but active development has shifted to Gateway API, and the tooling ecosystem is following.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-now-the-ingress-nginx-retirement">Why now: the Ingress-NGINX retirement<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#why-now-the-ingress-nginx-retirement" class="hash-link" aria-label="Direct link to Why now: the Ingress-NGINX retirement" title="Direct link to Why now: the Ingress-NGINX retirement" translate="no">​</a></h2>
<p>In November 2025, Kubernetes SIG Network and the Security Response Committee <a href="https://www.kubernetes.dev/blog/2025/11/12/ingress-nginx-retirement/" target="_blank" rel="noopener noreferrer">announced the retirement</a> of the Ingress-NGINX project. Upstream maintenance ended in <strong>March 2026</strong>.</p>
<p>Microsoft is providing a support bridge: critical security patches for the application routing add-on's managed NGINX implementation will continue through <strong>November 2026</strong>. But after that date, managed NGINX will no longer receive Azure support, and users will need to have migrated.</p>
<p>If your cluster uses the application routing add-on with NGINX today, the application routing Gateway API implementation is your recommended migration path.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="whats-new-gateway-api-support-in-the-app-routing-add-on">What's new: Gateway API support in the app routing add-on<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#whats-new-gateway-api-support-in-the-app-routing-add-on" class="hash-link" aria-label="Direct link to What's new: Gateway API support in the app routing add-on" title="Direct link to What's new: Gateway API support in the app routing add-on" translate="no">​</a></h2>
<p>The application routing add-on now supports the Kubernetes Gateway API through a new mode, enabled via <code>--enable-app-routing-istio</code>. Under the hood, this deploys a lightweight Istio control plane to manage the gateway infrastructure — but without enabling the full Istio service mesh. No sidecar injection, no Istio CRDs for your workloads. Just Istio doing what it does well: managing Envoy-based gateway proxies.</p>
<p>When you create a <code>Gateway</code> resource using the <code>approuting-istio</code> GatewayClass, AKS automatically provisions:</p>
<ul>
<li>An Envoy-based <strong>Deployment</strong> to handle traffic</li>
<li>A <strong>LoadBalancer Service</strong> exposing the gateway externally</li>
<li>A <strong>HorizontalPodAutoscaler</strong> (default: 2–5 replicas at 80% CPU)</li>
<li>A <strong>PodDisruptionBudget</strong> (minimum 1 available) for safe upgrades</li>
</ul>
<p>All of this is managed for you. You write a <code>Gateway</code> and an <code>HTTPRoute</code>, and AKS handles the underlying infrastructure.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="meshless-istio-how-it-differs-from-the-istio-service-mesh-add-on">Meshless Istio: how it differs from the Istio service mesh add-on<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#meshless-istio-how-it-differs-from-the-istio-service-mesh-add-on" class="hash-link" aria-label="Direct link to Meshless Istio: how it differs from the Istio service mesh add-on" title="Direct link to Meshless Istio: how it differs from the Istio service mesh add-on" translate="no">​</a></h3>
<p>If you're already using or considering the <a href="https://learn.microsoft.com/azure/aks/istio-about" target="_blank" rel="noopener noreferrer">Istio service mesh add-on</a>, it's worth understanding how this differs:</p>
<table><thead><tr><th>Feature</th><th>App routing Gateway API</th><th>Istio service mesh add-on</th></tr></thead><tbody><tr><td><strong>GatewayClass</strong></td><td><code>approuting-istio</code></td><td><code>istio</code></td></tr><tr><td><strong>Sidecar injection</strong></td><td>Not enabled</td><td>Enabled cluster-wide</td></tr><tr><td><strong>Istio CRDs</strong></td><td>Not installed</td><td>Installed</td></tr><tr><td><strong>Upgrades</strong></td><td>In-place (minor and patch)</td><td>Canary upgrades for minor versions</td></tr></tbody></table>
<p>The two cannot run simultaneously — enabling one requires the other to be disabled first. If you want Gateway API based ingress on a full service mesh (mTLS between services, traffic policies, telemetry), use the Istio service mesh add-on. If you just wish to have ingress via the Gateway API without Istio-specific features, this is the right choice.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="getting-started">Getting started<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#getting-started" class="hash-link" aria-label="Direct link to Getting started" title="Direct link to Getting started" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="prerequisites">Prerequisites<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h3>
<ol>
<li>
<p><strong><code>aks-preview</code> CLI extension</strong>, version <code>19.0.0b26</code> or later:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az extension add --name aks-preview</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az extension update --name aks-preview</span><br></span></code></pre></div></div>
</li>
<li>
<p><strong>Preview feature flags</strong> registered on your subscription:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az feature register --namespace "Microsoft.ContainerService" --name "ManagedGatewayAPIPreview"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">az feature register --namespace "Microsoft.ContainerService" --name "AppRoutingIstioGatewayAPIPreview"</span><br></span></code></pre></div></div>
</li>
</ol>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="enable-the-app-routing-gateway-api-implementation">Enable the app routing Gateway API implementation<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#enable-the-app-routing-gateway-api-implementation" class="hash-link" aria-label="Direct link to Enable the app routing Gateway API implementation" title="Direct link to Enable the app routing Gateway API implementation" translate="no">​</a></h3>
<p><strong>On a new cluster:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az aks create \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group ${RESOURCE_GROUP} \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name ${CLUSTER} \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-gateway-api \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-app-routing-istio</span><br></span></code></pre></div></div>
<p><strong>On an existing cluster:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">az aks update \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --resource-group ${RESOURCE_GROUP} \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --name ${CLUSTER} \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-gateway-api \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --enable-app-routing-istio</span><br></span></code></pre></div></div>
<p>After a moment, you should see <code>istiod</code> running in <code>aks-istio-system</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n aks-istio-system</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME                      READY   STATUS    RESTARTS   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">istiod-54b4ff45cf-htph8   1/1     Running   0          3m15s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">istiod-54b4ff45cf-wlvgd   1/1     Running   0          3m</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="deploy-a-sample-app-gateway-and-httproute">Deploy a sample app, Gateway, and HTTPRoute<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#deploy-a-sample-app-gateway-and-httproute" class="hash-link" aria-label="Direct link to Deploy a sample app, Gateway, and HTTPRoute" title="Direct link to Deploy a sample app, Gateway, and HTTPRoute" translate="no">​</a></h3>
<p>First, deploy the <code>httpbin</code> sample application:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.27/samples/httpbin/httpbin.yaml</span><br></span></code></pre></div></div>
<p>Then create a <code>Gateway</code> using the <code>approuting-istio</code> GatewayClass and attach an <code>HTTPRoute</code> to it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f - &lt;&lt;EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: httpbin-gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  gatewayClassName: approuting-istio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  listeners:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - name: http</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    port: 80</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    protocol: HTTP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    allowedRoutes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      namespaces:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        from: Same</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: HTTPRoute</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: httpbin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  parentRefs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - name: httpbin-gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  hostnames: ["httpbin.example.com"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  rules:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - matches:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - path:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        type: PathPrefix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        value: /get</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    backendRefs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - name: httpbin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      port: 8000</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<p>AKS will provision the underlying Deployment, Service, HPA, and PDB automatically. Wait for the Gateway to be programmed and retrieve its external IP:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl wait --for=condition=programmed gateways.gateway.networking.k8s.io httpbin-gateway --timeout=120s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export INGRESS_HOST=$(kubectl get gateways.gateway.networking.k8s.io httpbin-gateway -ojsonpath='{.status.addresses[0].value}')</span><br></span></code></pre></div></div>
<p>Send a request to verify traffic is flowing:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">curl -s -I -H "Host: httpbin.example.com" "http://$INGRESS_HOST/get"</span><br></span></code></pre></div></div>
<p>You should see an <code>HTTP 200</code> response.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="upgrades">Upgrades<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#upgrades" class="hash-link" aria-label="Direct link to Upgrades" title="Direct link to Upgrades" translate="no">​</a></h3>
<p>The Istio control plane version is tied to your AKS cluster's Kubernetes version — AKS automatically reconciles the latest supported Istio revision that is compatible with your cluster's Kubernetes version. Patch version upgrades happen automatically as part of AKS releases. Minor version upgrades happen in-place when you upgrade your cluster's Kubernetes version, or automatically when a new Istio minor version is released for your AKS version. To see which Istio revision your cluster will receive, consult the <a href="https://learn.microsoft.com/azure/aks/istio-support-policy#service-mesh-add-on-release-calendar" target="_blank" rel="noopener noreferrer">service mesh add-on release calendar</a>. You can also follow the <a href="https://github.com/azure/aks/releases" target="_blank" rel="noopener noreferrer">AKS release notes</a> to stay current.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</div><div class="admonitionContent_BuS1"><p>Unlike the Istio service mesh add-on, the app routing Gateway API implementation does <strong>not</strong> use canary revisions for minor version upgrades. Upgrades happen in-place. The HPA and PDB on each Gateway minimize disruptions, but plan accordingly for production workloads. If your cluster has a <a href="https://learn.microsoft.com/azure/aks/planned-maintenance" target="_blank" rel="noopener noreferrer">maintenance window</a> configured, in-place upgrades of the <code>istiod</code> deployment will respect it.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="current-limitations">Current limitations<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#current-limitations" class="hash-link" aria-label="Direct link to Current limitations" title="Direct link to Current limitations" translate="no">​</a></h2>
<ul>
<li><strong>DNS and TLS certificate management</strong> via the app routing add-on is not yet supported for Gateway API, and won't be until the feature reaches GA. This is a meaningful difference from the existing NGINX-based add-on, which automates Key Vault and Azure DNS integration through <code>az aks approuting update</code> and <code>az aks approuting zone add</code>. In the meantime, TLS termination is possible but requires manual setup — see <a href="https://learn.microsoft.com/azure/aks/app-routing-gateway-api-tls" target="_blank" rel="noopener noreferrer">Secure ingress traffic with the application routing Gateway API implementation</a> for steps.</li>
<li><strong>SNI passthrough</strong> (<code>TLSRoute</code>) and <strong>egress traffic management</strong> are not supported.</li>
<li><strong>Mutually exclusive</strong> with the Istio service mesh add-on — the two cannot run simultaneously.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="next-steps">Next steps<a href="https://blog.aks.azure.com/2026/03/18/app-routing-gateway-api#next-steps" class="hash-link" aria-label="Direct link to Next steps" title="Direct link to Next steps" translate="no">​</a></h2>
<ul>
<li><strong>Try it</strong>: Follow the <a href="https://learn.microsoft.com/azure/aks/app-routing-gateway-api" target="_blank" rel="noopener noreferrer">application routing Gateway API quickstart</a>.</li>
<li><strong>Secure it</strong>: Configure <a href="https://learn.microsoft.com/azure/aks/app-routing-gateway-api-tls" target="_blank" rel="noopener noreferrer">TLS termination with Azure Key Vault</a>.</li>
<li><strong>Share feedback</strong>: Open an issue in <a href="https://github.com/Azure/AKS/issues" target="_blank" rel="noopener noreferrer">AKS GitHub Issues</a>.</li>
</ul>]]></content:encoded>
            <category>App Routing</category>
            <category>Gateway API</category>
            <category>Networking</category>
            <category>Istio</category>
            <category>Ingress NGINX</category>
        </item>
        <item>
            <title><![CDATA[Scaling multi-node LLM inference with NVIDIA Dynamo and NVIDIA GPUs on AKS (Part 3)]]></title>
            <link>https://blog.aks.azure.com/2026/03/16/dynamo-on-aks-part-3</link>
            <guid>https://blog.aks.azure.com/2026/03/16/dynamo-on-aks-part-3</guid>
            <pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how NVIDIA Dynamo's KV Router optimizes multi-worker LLM inference on AKS with H100 GPUs, achieving lower latency through intelligent routing.]]></description>
            <content:encoded><![CDATA[<p><em>This blog post is co-authored with <a href="https://www.linkedin.com/in/nikharmaheshwari/" target="_blank" rel="noopener noreferrer">Nikhar Maheshwari</a>,
<a href="https://www.linkedin.com/in/anish-maddipoti/" target="_blank" rel="noopener noreferrer">Anish Maddipoti</a>,
<a href="https://www.linkedin.com/in/rohan-s-varma/" target="_blank" rel="noopener noreferrer">Rohan Varma</a>,
<a href="https://www.linkedin.com/in/clement-ai/" target="_blank" rel="noopener noreferrer">Clement Pakkam Isaac</a>, and
<a href="https://www.linkedin.com/in/stephen-mcc/?lipi=urn%3Ali%3Apage%3Ad_flagship3_profile_view_base%3BEwyhTwyCTTOp9G8dfPVssw%3D%3D" target="_blank" rel="noopener noreferrer">Stephen Mccoulough</a>
from NVIDIA.</em></p>
<p>We kicked things off in <a href="https://blog.aks.azure.com/2025/10/24/dynamo-on-aks" target="_blank" rel="noopener noreferrer">Part 1</a> by introducing NVIDIA Dynamo on AKS and demonstrating 1.2 million tokens per second across 10 GPU nodes of GB200 NVL72. In <a href="https://blog.aks.azure.com/2026/01/22/dynamo-on-aks-part-2" target="_blank" rel="noopener noreferrer">Part 2</a>, we explored the Dynamo Planner and Profiler for SLO-driven scaling.</p>
<p>In this blog, we explore how <strong>Dynamo’s KV Router</strong> makes multi-worker LLM deployments significantly more efficient, demonstrating over <strong>20x faster</strong> Time To First Token (TTFT) and over <strong>4x faster</strong> end-to-end latency on real-world production traces. These latency reductions not only improve the end-user experience but also maximize GPU utilization and lower the Total Cost of Ownership (TCO).</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-challenge-the-hidden-cost-of-redundant-compute">The Challenge: The Hidden Cost of Redundant Compute<a href="https://blog.aks.azure.com/2026/03/16/dynamo-on-aks-part-3#the-challenge-the-hidden-cost-of-redundant-compute" class="hash-link" aria-label="Direct link to The Challenge: The Hidden Cost of Redundant Compute" title="Direct link to The Challenge: The Hidden Cost of Redundant Compute" translate="no">​</a></h2>
<p>As enterprise Generative AI evolves into complex multi-agent workflows, system prompts and shared context windows have significantly increased in size. The compute-intensive process of reading this context and building the <strong>Key-Value (KV) cache</strong>, known as the <strong>prefill</strong> phase, introduces a bottleneck.</p>
<p>In production, many requests share an initial segment of tokens (that is, the <strong>prefix</strong>), such as a system prompt defining an agent’s persona, a standard compliance disclaimer, a shared document in a retrieval-augmented generation (RAG) workflow, or conversation history. If a worker already holds the KV cache for that prefix, it can reuse the cache for the next matching request—reducing prefill and reaching the first output token faster.</p>
<p>Standard routing strategies such as round-robin, or policies optimized purely for load balancing, spread requests across workers without considering KV-cache locality. As a result, requests with the same prefix may land on different workers over time, forcing KV cache rebuilds on workers that don’t currently have the relevant cached blocks.</p>
<p>For globally shared prefixes (e.g., a system prompt), this is acceptable: each worker quickly builds and retains the KV cache for that prefix, so routing decisions don’t materially change prefill cost. But many prefixes are shared only within subsets of traffic, forming <strong>“prefix families.”</strong> Requests in the same prefix family may be tied to the same document, agent configuration, or conversation history. When load balancers scatter these related requests across the worker pool, cache reuse drops and the same prefixes get rebuilt repeatedly, increasing latency.</p>
<p>Simply routing to the worker with the best cache hit is not ideal either. Doing so can overload cache-rich workers while others sit idle, creating queue buildup that hurts tail latency just as much as redundant prefill.</p>
<p>This makes routing inference requests a two-signal decision:</p>
<ol>
<li>
<p><strong>Cache locality/prefill cost</strong>: Which worker can serve this request with the least remaining prefill because it already has relevant cached KV blocks?</p>
</li>
<li>
<p><strong>Worker load</strong>: Which worker can take the request without becoming a decode/queue bottleneck?</p>
</li>
</ol>
<p>A routing strategy that optimizes only one of these signals leaves performance on the table.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-solution-dynamo-kv-router">The Solution: Dynamo KV Router<a href="https://blog.aks.azure.com/2026/03/16/dynamo-on-aks-part-3#the-solution-dynamo-kv-router" class="hash-link" aria-label="Direct link to The Solution: Dynamo KV Router" title="Direct link to The Solution: Dynamo KV Router" translate="no">​</a></h2>
<p><a href="https://docs.nvidia.com/dynamo/components/router" target="_blank" rel="noopener noreferrer">Dynamo's KV Router</a> solves this by making the routing layer "state-aware". This intelligence is designed to drop into your existing inference stack with minimal friction.</p>
<ul>
<li>
<p><strong>Engine-Agnostic Design</strong>: The router works out-of-the-box with major engines like <a href="https://docs.vllm.ai/" target="_blank" rel="noopener noreferrer">vLLM</a>, <a href="https://docs.nvidia.com/tensorrt-llm/index.html" target="_blank" rel="noopener noreferrer">TensorRT-LLM</a>, and <a href="https://docs.sglang.io/" target="_blank" rel="noopener noreferrer">SGLang</a>. Workers automatically broadcast their cache state (KV events) to the router, requiring no complex API instrumentation or engine-specific configuration changes.</p>
</li>
<li>
<p><strong>Intelligent, Tunable Routing</strong>: The router doesn't just look for cache hits; it uses a cost function that balances <strong>cache locality</strong> against <strong>worker load</strong>. This is fully configurable, allowing you to tune the router's behavior to favor cache reuse (ideal for prefill-heavy workloads) or load distribution (ideal for decode-heavy workloads) depending on your inference traffic patterns.</p>
</li>
</ul>
<p>To see this in practice, let’s take a look at how the router makes a decision in real-time.</p>
<p><img decoding="async" loading="lazy" alt="Dynamo KV Aware Routing decision among 3 workers" src="https://blog.aks.azure.com/assets/images/dynamo_kv_aware_router_diagram-a2d6faf5ae2d256754a733c2365b7413.png" width="1516" height="754" class="img_ev3q"></p>
<p>The router scores each worker using the following cost function:</p>
<p><code>Cost = overlap_weight × Prefill_Blocks + Decode_Blocks</code>,</p>
<p>and routes to the worker with the lowest cost. <em>Prefill blocks</em> are the blocks corresponding to input tokens not yet cached on the worker. The <em>overlap_weight</em> (default: 1.0) controls the tradeoff: higher overlap weight values penalize cache misses more heavily, favoring cache locality; lower values let decode load dominate, spreading requests more evenly.</p>
<p>In the above scenario, Worker 3 has the best cache hit rate (8 of 10 blocks cached), but its high decode load pushes its cost above Worker 2:</p>
<ul>
<li>Worker 1: 1.0 × 8 + 10 = <strong>18</strong></li>
<li>Worker 2: 1.0 × 5 + 5 = <strong>10</strong> <em>(selected — lowest cost)</em></li>
<li>Worker 3: 1.0 × 2 + 9 = <strong>11</strong></li>
</ul>
<p>The router takes the partial cache hit on Worker 2 to speed up prefill, while avoiding the contention on Worker 3. This prevents cache-rich but overloaded nodes from becoming hotspots. In the next section, we put this to the test on a real-world workload.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="validating-the-approach-benchmarks-on-aks">Validating the Approach: Benchmarks on AKS<a href="https://blog.aks.azure.com/2026/03/16/dynamo-on-aks-part-3#validating-the-approach-benchmarks-on-aks" class="hash-link" aria-label="Direct link to Validating the Approach: Benchmarks on AKS" title="Direct link to Validating the Approach: Benchmarks on AKS" translate="no">​</a></h2>
<p>To quantify the value of inference with the KV-aware router, we ran an apples-to-apples comparison on an AKS cluster using <a href="https://github.com/ai-dynamo/dynamo" target="_blank" rel="noopener noreferrer">Dynamo</a> and <a href="https://github.com/ai-dynamo/grove" target="_blank" rel="noopener noreferrer">Grove</a>, keeping the hardware, model, and traffic identical while changing <em>only</em> the routing strategy from Round-Robin to KV-Aware.</p>
<p>In the test environment, we deployed the <a href="https://huggingface.co/Qwen/Qwen3-32B" target="_blank" rel="noopener noreferrer">Qwen3-32B</a> LLM on an AKS cluster with 8x NVIDIA H100 GPUs (4 nodes of Azure <code>Standard_NC80adis_H100_v5</code>). To simulate a production workload, we replayed ~23,000 requests from the <a href="https://github.com/kvcache-ai/Mooncake/blob/main/FAST25-release/traces/toolagent_trace.jsonl" target="_blank" rel="noopener noreferrer">Mooncake Tool Agent Traces</a> dataset with a fixed schedule, preserving the original request arrival pattern.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>These traces are sampled from <a href="https://www.moonshot.ai/" target="_blank" rel="noopener noreferrer">Moonshot AI</a>'s Kimi production cluster and capture workloads related to tool-calling and agent functionality. Such workloads are characterized by the use of long, pre-designed system prompts that are fully repetitive. For example, an AI agent with access to multiple tools must include a detailed definition of each tool (name, parameters, and calling conventions) at the start of each request. This setup results in a high degree of prefix sharing across requests, making it an apt scenario to evaluate how well cache-aware routing performs.</p></div></div>
<p>The following table summarizes key performance metrics across the two deployments:</p>
<table><thead><tr><th>Metric</th><th>Round-Robin</th><th>Dynamo KV Router</th><th>Speedup</th></tr></thead><tbody><tr><td>TTFT avg.</td><td>53,877 ms</td><td>2,658 ms</td><td><strong>~20.4x</strong></td></tr><tr><td>TTFT P99</td><td>280,221 ms</td><td>17,585 ms</td><td><strong>~15.9x</strong></td></tr><tr><td>E2E Latency avg.</td><td>84,517 ms</td><td>19,761 ms</td><td><strong>~4.3x</strong></td></tr><tr><td>E2E Latency P99</td><td>340,006 ms</td><td>90,299 ms</td><td><strong>~3.8x</strong></td></tr></tbody></table>
<p>As shown in the results above, we see the advantage of routing requests to workers that already hold the matching prefix KV cache. By eliminating the hidden cost of redundant compute, the Dynamo KV Router demonstrated two key benefits in our tests:</p>
<ul>
<li>
<p><strong>Users received faster responses</strong>: 20x faster TTFT means that users experienced significantly less wait time.</p>
</li>
<li>
<p><strong>Congestion dropped sharply</strong>: 4x faster end-to-end latency resulted in less time spent re-computing prefixes, so workers completed requests sooner and prevented the queue pileups that typically degrade performance during traffic spikes.</p>
</li>
</ul>
<p>To explore further:</p>
<ul>
<li>
<p>Follow the <a href="https://aka.ms/aks-dynamo" target="_blank" rel="noopener noreferrer">AKS Dynamo deployment guide</a> to enable it in your environment.</p>
</li>
<li>
<p>Check out the <a href="https://docs.nvidia.com/dynamo/v-0-9-0/user-guides/kv-cache-aware-routing" target="_blank" rel="noopener noreferrer">Dynamo Router Guide</a> to learn about advanced configuration options and fine-tune performance.</p>
</li>
<li>
<p>Refer to the <a href="https://aka.ms/dynamo-on-aks-benchmarking" target="_blank" rel="noopener noreferrer">Benchmarking Guide</a> to reproduce these results and compare in your setup.</p>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="looking-ahead-orchestration--data-mover-nixl">Looking Ahead: Orchestration &amp; Data Mover (NIXL)<a href="https://blog.aks.azure.com/2026/03/16/dynamo-on-aks-part-3#looking-ahead-orchestration--data-mover-nixl" class="hash-link" aria-label="Direct link to Looking Ahead: Orchestration &amp; Data Mover (NIXL)" title="Direct link to Looking Ahead: Orchestration &amp; Data Mover (NIXL)" translate="no">​</a></h2>
<p>In this blog series, we’ve used NVIDIA Dynamo to establish the foundations for high-performance LLM serving—introducing disaggregated architectures in <a href="https://blog.aks.azure.com/2025/10/24/dynamo-on-aks" target="_blank" rel="noopener noreferrer">Part 1</a>, SLO-driven planning in <a href="https://blog.aks.azure.com/2026/01/22/dynamo-on-aks-part-2" target="_blank" rel="noopener noreferrer">Part 2</a>, and now intelligent routing. Turning these systems into production deployments requires two additional pieces: efficient data movement across the memory hierarchy between components and purpose-built orchestration.</p>
<p>In disaggregated inference deployments, the KV cache must be transferred between prefill and decode workers, passing the computed context from the GPUs that process the prompt to the GPUs that generate the response. <a href="https://github.com/ai-dynamo/nixl" target="_blank" rel="noopener noreferrer">NVIDIA Inference Xfer Library</a> (NIXL) is a high-bandwidth, low-latency library purpose-built for this task, and now, with the <a href="https://github.com/ai-dynamo/nixl/blob/release/0.10.0/src/plugins/azure_blob/README.md" target="_blank" rel="noopener noreferrer">NIXL plugin for Azure Blob Storage</a>, Dynamo’s disaggregated data plane can also leverage Azure Blob Storage–native integration for data movement.</p>
<p>To leverage NIXL’s speed and minimize transfer time, placement needs to reflect the underlying physical topology (e.g., host/GPU affinity and network proximity). As the Kubernetes-native orchestrator for Dynamo, <strong>NVIDIA Grove</strong> coordinates role-aware startup and component readiness. By leveraging Azure topology signals exposed to AKS (reflecting the physical network layout), Grove can schedule tightly communicating pods in closer physical proximity. This helps prevent operational “pileups,” providing a robust foundation for scaling advanced inference setups.</p>
<p>With Grove and NIXL, Dynamo’s disaggregated serving becomes much easier to run at scale on AKS, and we will discuss them in further detail later in this series!</p>]]></content:encoded>
            <category>Dynamo on AKS series</category>
            <category>AI</category>
            <category>Performance</category>
            <category>Open Source</category>
        </item>
    </channel>
</rss>