Artifact Streaming lets an AKS node retrieve the image content needed to start a container before downloading the entire image. The benefit is most visible with large images and node scale-out or bulk Pod startup. This guide retains the source's July 2026 support snapshot: Linux AMD64 availability is described as GA, with Azure CLI 2.87.0 or later required. A locally tested 2.86.0 CLI still printed Preview warnings. Verify the current official support matrix and your installed CLI before rollout; these are documented prerequisites, not measurements performed by this site.
1. Check eligibility and exclusions
| Component | Requirement |
|---|---|
| Azure CLI | 2.87.0 or later |
| AKS | Kubernetes 1.25 or later |
| ACR | Premium SKU |
| Node operating system | Ubuntu 20.04 or later, or Azure Linux |
| Image architecture | Linux AMD64 |
| Authentication | AKS–ACR integration using the kubelet identity, normally configured with --attach-acr |
| Operator permissions | Azure Kubernetes Service Contributor for node-pool configuration; registry operations require their own appropriate permissions |
- Windows and ARM64 images are unsupported; only the AMD64 part of a multi-architecture image is a streaming candidate.
- Standard and Basic ACR tiers do not expose this feature.
- The source excludes customer-managed-key encrypted registries and geo-replicated registry artifact generation/synchronization.
- Pull by tag, not an image digest such as
image@sha256:.... GitOps or Flux digest pinning prevents streaming in the documented scenario. - Pulls authenticated with
imagePullSecrets, non-Entra scope tokens, or ACR admin credentials fall back to a normal pull. A successful pull therefore does not itself prove streaming. - Images over 30 GB are not described as technically prohibited, but the source warns that perceived gains diminish. Prefer mounted volumes for very large static data.
Set the following to your own resources. Commands are Bash examples, not commands executed during publication.
export RESOURCE_GROUP="YOUR_RESOURCE_GROUP"
export CLUSTER="YOUR_CLUSTER"
export LOCATION="YOUR_AZURE_REGION"
export ACR_NAME="YOUR_ACR_NAME"
export REPOSITORY="YOUR_REPOSITORY"
export TAG="YOUR_IMAGE_TAG"
export NODEPOOL="YOUR_NODEPOOL"
az --version
# Upgrade when below the required version.
az upgrade
az aks show -g "$RESOURCE_GROUP" -n "$CLUSTER" --query kubernetesVersion -o tsv
az acr show --name "$ACR_NAME" --query sku.name -o tsv
az aks check-acr --resource-group "$RESOURCE_GROUP" --name "$CLUSTER" --acr "$ACR_NAME.azurecr.io"
az aks nodepool list -g "$RESOURCE_GROUP" --cluster-name "$CLUSTER" \
--query "[].{pool:name, osType:osType, osSKU:osSKU}" -o table
kubectl get deployments -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\t"}{range .spec.template.spec.containers[*]}{.image}{" "}{end}{"\n"}{end}' | grep '@sha256'
# Confirm architecture as well; the source's node-pool query does not show it.
kubectl get nodes -L kubernetes.io/arch,kubernetes.io/os2. Establish a cold-pull baseline
Measure before enabling streaming. Use the same image and node VM size in both experiments, and use fresh nodes or nodes without the tested image. Reusing cached layers can erase or fabricate the apparent improvement. Repeat at least three times under equivalent cold-cache conditions and average the results; a single observation is sensitive to network variation.
Create an isolated test node pool
az aks nodepool add -g "$RESOURCE_GROUP" --cluster-name "$CLUSTER" \
--name streamtest --node-count 1 --node-vm-size Standard_D4ds_v5 \
--mode User --os-sku Ubuntu
# Optional isolation: only Pods with the matching toleration can run here.
az aks nodepool update -g "$RESOURCE_GROUP" --cluster-name "$CLUSTER" \
--name streamtest --node-taints stream-test=true:NoScheduleMeasure submission, image pull, and readiness separately
# Record the timestamp BEFORE submitting the Pod.
date -u +"%Y-%m-%dT%H:%M:%S.%3NZ"
kubectl run streaming-test --image="$ACR_NAME.azurecr.io/$REPOSITORY:$TAG" \
--overrides='{"spec":{"tolerations":[{"key":"stream-test","operator":"Equal","value":"true","effect":"NoSchedule"}],"nodeSelector":{"agentpool":"streamtest"}}}' \
--restart=Never -n default
time kubectl wait --for=condition=Ready pod/streaming-test -n default --timeout=600s
kubectl get events -n default --field-selector involvedObject.name=streaming-test \
--sort-by=.lastTimestamp \
-o custom-columns=REASON:.reason,TIME:.lastTimestamp,MESSAGE:.message
az acr repository show --name "$ACR_NAME" --image "$REPOSITORY:$TAG" --query "imageSize" -o tsv
kubectl delete pod streaming-test -n default| Metric | How to record it |
|---|---|
| Image pull time | Difference between Pulling and Pulled event timestamps |
| Total time to Ready | Pod submission timestamp to successful Ready completion; the duration of kubectl wait alone starts after submission and is not the complete total |
| Container start | Started event, distinct from application readiness |
| Image size | Registry-reported imageSize in bytes; convert to MB if present. If this query returns no field, inspect supported manifest metadata instead of treating a blank as zero. |
| Node conditions | VM size, pool, OS, architecture, whether the node is new, and cache state |
The source records the submission timestamp after creating the Pod. The sequence above moves it before creation to avoid understating time to Ready. Event timestamps and aggregation may be coarse; preserve raw timing evidence and use consistent measurement methods.
3. Prepare the registry artifact
Create resources only if they do not exist. The default registry setting changes the CLI configuration for subsequent commands. Import or push an image, create its streaming artifact, and inspect referrers. Repository-level automatic artifact generation is optional.
az group create --name "$RESOURCE_GROUP" --location "$LOCATION"
az acr create --resource-group "$RESOURCE_GROUP" --name "$ACR_NAME" --sku Premium
az configure --defaults acr="$ACR_NAME"
# Skip import if the target image already exists.
az acr import --source "docker.io/YOUR_SOURCE_REPOSITORY:YOUR_SOURCE_TAG" --image "$REPOSITORY:$TAG"
az acr artifact-streaming create --image "$REPOSITORY:$TAG"
# Optional: generate streaming artifacts for future images in this repository.
az acr artifact-streaming update --name "$ACR_NAME" --repository "$REPOSITORY" --enable-streaming true
az acr manifest list-referrers --name "$REPOSITORY:$TAG" --registry "$ACR_NAME"4. Enable a node pool and verify the profile
Choose either new-pool creation or update of an existing pool; do not run both indiscriminately. Ensure the workload is actually scheduled onto the enabled pool.
# New pool:
az aks nodepool add --resource-group "$RESOURCE_GROUP" \
--cluster-name "$CLUSTER" --name "$NODEPOOL" --enable-artifact-streaming
# OR an existing pool:
az aks nodepool update --resource-group "$RESOURCE_GROUP" \
--cluster-name "$CLUSTER" --name "$NODEPOOL" --enable-artifact-streaming
az aks nodepool show --resource-group "$RESOURCE_GROUP" \
--cluster-name "$CLUSTER" --name "$NODEPOOL" --query artifactStreamingProfile
# Expected profile: enabled is true.5. Repeat the experiment after enablement
Use the identical image, VM size, timing procedure, and number of repetitions on uncached streaming-enabled nodes. If the pool has the test taint, retain its toleration as shown. Deleting only the Pod does not remove cached image layers.
date -u +"%Y-%m-%dT%H:%M:%S.%3NZ"
kubectl run streaming-test-after --image="$ACR_NAME.azurecr.io/$REPOSITORY:$TAG" \
--overrides="{\"spec\":{\"tolerations\":[{\"key\":\"stream-test\",\"operator\":\"Equal\",\"value\":\"true\",\"effect\":\"NoSchedule\"}],\"nodeSelector\":{\"agentpool\":\"$NODEPOOL\"}}}" \
--restart=Never -n default
time kubectl wait --for=condition=Ready pod/streaming-test-after -n default --timeout=600s
kubectl get events -n default --field-selector involvedObject.name=streaming-test-after \
--sort-by=.lastTimestamp \
-o custom-columns=REASON:.reason,TIME:.lastTimestamp,MESSAGE:.message
kubectl delete pod streaming-test-after -n default| Metric | Before: average of at least three cold runs | After: average of at least three cold runs | Improvement |
|---|---|---|---|
| Pulling to Pulled | Record measurement | Record measurement | (Before − After) / Before × 100% |
| Submission to Ready | Record measurement | Record measurement | (Before − After) / Before × 100% |
No numerical speed-up was supplied in the source: this is a benchmark template, not a claim of measured performance. Large images of hundreds of MB to several GB are more likely to show a benefit than images of only tens of MB. Recheck tag-based deployment and kubelet identity authentication before interpreting an unexpectedly small gain.
6. Roll back and remove test resources
az aks nodepool update --resource-group "$RESOURCE_GROUP" \
--cluster-name "$CLUSTER" --name "$NODEPOOL" --disable-artifact-streaming
# Optional and destructive: remove only the dedicated test pool after confirming it has no needed workloads.
az aks nodepool delete -g "$RESOURCE_GROUP" --cluster-name "$CLUSTER" --name streamtest --no-waitDisabling the node-pool feature is the documented rollback. This is separate from removing registry referrers or changing repository automatic-generation settings; the source does not provide a registry cleanup procedure.
References and source boundary
The article preserves the source's feature restrictions and version requirements. Reconfirm them against the service and CLI version you deploy; no Azure resources or source commands were run to verify this publication.
Resources
No standalone companion files are distributed for this topic. Use the in-page examples and review the editorial notes below for source availability.
Editorial notes
This page consolidates the following source documents into an English technical guide:
- AKS Artifact Streaming (Linux) Deployment Guide
- Private environment identifiers are replaced with input variables; no original environment values or private source links are published.
- The repository root README is only an index and adds no technical procedure.
- No standalone YAML resource exists in this source folder, so no download has been invented.
No original credentials or private repository links are included. Do not put populated configuration files or copied production outputs back into this public site.