Azure AKS: Production-Ready Cluster Setup in 15 Minutes
Azure AKS: Production-Ready Cluster Setup in 15 Minutes
Most AKS tutorials show you the bare minimum — a single az aks create command that spins up a cluster nobody would trust in production. This guide skips the toy examples and walks you through everything you actually need: private networking, autoscaling node pools, Azure AD RBAC integration, and observability wired up from day one.
Prerequisites
- Azure CLI 2.50+ installed and logged in (
az login) - An active subscription with Contributor or Owner access
- A resource group and virtual network already provisioned (or follow the commands below)
Step 1: Foundation — Resource Group and Networking
# Variables — adjust to your environment
RESOURCE_GROUP="rg-aks-prod"
LOCATION="eastus2"
CLUSTER_NAME="aks-prod-01"
VNET_NAME="vnet-aks-prod"
SUBNET_NAME="snet-aks-nodes"
# Create resource group
az group create --name $RESOURCE_GROUP --location $LOCATION
# Create VNet with a /16 range
az network vnet create \
--resource-group $RESOURCE_GROUP \
--name $VNET_NAME \
--address-prefix 10.10.0.0/16 \
--subnet-name $SUBNET_NAME \
--subnet-prefix 10.10.1.0/24
# Grab the subnet ID for later
SUBNET_ID=$(az network vnet subnet show \
--resource-group $RESOURCE_GROUP \
--vnet-name $VNET_NAME \
--name $SUBNET_NAME \
--query id -o tsv)
Keeping the subnet dedicated to AKS nodes avoids IP exhaustion issues when pods scale up. Use Azure CNI (not kubenet) so pods get real VNet IPs and network policies work properly.
Step 2: Create the AKS Cluster
az aks create \
--resource-group $RESOURCE_GROUP \
--name $CLUSTER_NAME \
--location $LOCATION \
--kubernetes-version 1.29 \
--node-count 3 \
--node-vm-size Standard_D4ds_v5 \
--os-disk-size-gb 128 \
--vnet-subnet-id $SUBNET_ID \
--network-plugin azure \
--network-policy calico \
--enable-cluster-autoscaler \
--min-count 2 \
--max-count 10 \
--enable-aad \
--enable-azure-rbac \
--enable-managed-identity \
--enable-addons monitoring \
--workspace-resource-id "/subscriptions/<sub-id>/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.OperationalInsights/workspaces/law-aks-prod" \
--zones 1 2 3 \
--uptime-sla \
--generate-ssh-keys
Key flags explained:
| Flag | Why It Matters |
|---|---|
--network-policy calico | Enables pod-level firewall rules |
--enable-azure-rbac | Maps Azure AD groups to Kubernetes RBAC roles |
--zones 1 2 3 | Spreads nodes across availability zones |
--uptime-sla | Guarantees 99.95% API server uptime (paid) |
--enable-addons monitoring | Deploys Container Insights agent automatically |
Step 3: Add a Spot Node Pool for Batch Workloads
Your system pool handles critical services. Add a spot pool for cost-sensitive batch jobs:
az aks nodepool add \
--resource-group $RESOURCE_GROUP \
--cluster-name $CLUSTER_NAME \
--name spotpool \
--node-count 0 \
--node-vm-size Standard_D8ds_v5 \
--priority Spot \
--eviction-policy Delete \
--spot-max-price -1 \
--enable-cluster-autoscaler \
--min-count 0 \
--max-count 20 \
--node-taints "kubernetes.azure.com/scalesetpriority=spot:NoSchedule" \
--labels spot=true
Pods that tolerate spot nodes use this cheaper pool. Everything else stays on the system pool. Scale-to-zero (--min-count 0) means you pay nothing when the pool is idle.
Step 4: Connect kubectl and Verify
az aks get-credentials \
--resource-group $RESOURCE_GROUP \
--name $CLUSTER_NAME \
--overwrite-existing
kubectl get nodes -o wide
Expected output:
NAME STATUS ROLES AGE VERSION INTERNAL-IP
aks-nodepool1-12345678-vmss000000 Ready <none> 2m v1.29.2 10.10.1.4
aks-nodepool1-12345678-vmss000001 Ready <none> 2m v1.29.2 10.10.1.5
aks-nodepool1-12345678-vmss000002 Ready <none> 2m v1.29.2 10.10.1.6
Step 5: RBAC — Assign Azure AD Groups to Cluster Roles
# Get the cluster resource ID
AKS_ID=$(az aks show \
--resource-group $RESOURCE_GROUP \
--name $CLUSTER_NAME \
--query id -o tsv)
# Grant an AD group cluster-admin access
az role assignment create \
--role "Azure Kubernetes Service RBAC Cluster Admin" \
--assignee-object-id "<ad-group-object-id>" \
--scope $AKS_ID
# Grant a dev team read-only access to a namespace
az role assignment create \
--role "Azure Kubernetes Service RBAC Reader" \
--assignee-object-id "<dev-team-group-id>" \
--scope "$AKS_ID/namespaces/app-production"
This eliminates kubeconfig sharing. Developers authenticate via az login and get only what their AD group allows.
Step 6: Deploy a Test Workload with Pod Disruption Budget
# test-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-test
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: nginx-test
template:
metadata:
labels:
app: nginx-test
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: nginx-test
containers:
- name: nginx
image: nginx:1.25
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: nginx-test-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: nginx-test
kubectl apply -f test-deploy.yaml
kubectl get pods -o wide
The topologySpreadConstraints ensures pods land in different availability zones, and the PDB prevents node drains from taking your entire deployment offline simultaneously.
Step 7: Enable Workload Identity (Replacing Pod Identity)
# Enable OIDC issuer and workload identity
az aks update \
--resource-group $RESOURCE_GROUP \
--name $CLUSTER_NAME \
--enable-oidc-issuer \
--enable-workload-identity
# Get the OIDC issuer URL
OIDC_ISSUER=$(az aks show \
--resource-group $RESOURCE_GROUP \
--name $CLUSTER_NAME \
--query "oidcIssuerProfile.issuerUrl" -o tsv)
echo "OIDC Issuer: $OIDC_ISSUER"
Workload identity lets pods authenticate to Azure services (Key Vault, Storage, SQL) without storing credentials in secrets. It's the modern replacement for pod-managed identity.
Production Checklist
Before you call this cluster production-ready, verify these items:
| Check | Command |
|---|---|
| API server accessible | kubectl cluster-info |
| All nodes Ready | kubectl get nodes |
| Container Insights data flowing | Azure Portal > AKS > Insights |
| Calico network policies installed | kubectl get pods -n kube-system | grep calico |
| Spot pool at zero when idle | kubectl get nodes -l spot=true |
| PDB on all critical deployments | kubectl get pdb -A |
| Workload identity enabled | az aks show ... --query "oidcIssuerProfile" |
Cost Estimate
For a production cluster in East US 2 with 3 x Standard_D4ds_v5 nodes plus autoscaling to 10:
| Component | Monthly Estimate |
|---|---|
| 3 system nodes (always on) | ~$420 |
| AKS uptime SLA | ~$73 |
| Log Analytics workspace | ~$30 |
| Spot nodes (20% utilization) | ~$40 |
| Total | ~$563/month |
Spot savings alone can cut 60-80% off node costs for batch workloads.
What's Next
This cluster is functional, but a real production environment also needs:
- Ingress controller: NGINX or Application Gateway Ingress Controller (AGIC)
- Certificate management: cert-manager with Let's Encrypt or Key Vault integration
- GitOps: Flux v2 or Argo CD for declarative deployments
- Image scanning: Defender for Containers or Trivy in your CI pipeline
- Backup: Velero with Azure Blob Storage for etcd and PV snapshots
The foundation you built here — CNI networking, Azure AD RBAC, autoscaling, and availability zones — is what separates a demo cluster from one your on-call team can actually sleep through.
Was this article helpful?
Related Articles
Azure AKS: Production Kubernetes Cluster Setup and Configuration
Deploy a production AKS cluster on Azure — node pools, managed identities, Azure CNI, RBAC integration with Entra ID, ACR integration, autoscaling, monitoring with Azure Monitor, and Terraform setup.
Azure Core Services: The DevOps Engineer's Essential Guide
Understand Azure's essential services — VMs, Storage, VNets, Azure AD (Entra ID), AKS, App Service, and Azure DevOps for infrastructure automation.
Fix Azure 'SubscriptionNotRegistered' Error
Resolve the Azure 'SubscriptionNotRegistered for resource type' error by registering the required resource provider with step-by-step instructions.
Azure Blob Storage 403 AuthorizationPermissionMismatch: Step-by-Step Fix Guide
If you've ever stared at this error message in your logs, you know the frustration: It's one of those errors that looks simple on the surface but has about...
Azure DevOps Pipelines: YAML CI/CD from Build to Production
How to build production Azure DevOps YAML pipelines — multi-stage CI/CD, environments, approvals, variable groups, templates, container jobs, and deployment strategies for Kubernetes and App Service.
Azure DevOps Pipelines: YAML Templates and Best Practices
Master Azure DevOps YAML pipelines with reusable templates, environment approvals, and multi-stage deployments.