Deploy AI agent teams to Azure Kubernetes Service using Helm, with Azure-native security (Workload Identity, Key Vault), KEDA autoscaling, and GitHub Actions CI/CD.
What is Squad? An AI team framework where specialized agents (Lead, Frontend, Backend, Tester, Monitor) collaborate on GitHub issues. Ralph is the work monitor that polls for new issues and dispatches work. Learn more β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β AKS Cluster β
β β
β ββββββββββββββββ ββββββββββββββββββββββββββ β
β β Ralph β β Agent Pods β β
β β (CronJob) βββββΆβ (spawned on demand) β β
β β */5 * * * * β β Picard, Data, Worf... β β
β ββββββββ¬ββββββββ ββββββββββββββββββββββββββ β
β β β
β ββββββββΌββββββββ ββββββββββββββββββββββββββ β
β β K8s Secrets ββββββ Key Vault CSI Driver β β
β β (GH_TOKEN) β β (Workload Identity) β β
β ββββββββββββββββ ββββββββββββββββββββββββββ β
β β
β ββββββββββββββββ ββββββββββββββββββββββββββ β
β β KEDA β β Prometheus Metrics β β
β β (autoscaler) ββββββ (optional) β β
β ββββββββββββββββ ββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
βΌ βΌ
GitHub Issues Azure Key Vault
(work queue) (secrets store)
Key design decisions:
- Ralph = CronJob β polls every 5 min, no always-on pod,
concurrencyPolicy: Forbidreplaces mutex - Agents = Jobs β spawned on demand, terminated when done (cost efficient)
- Secrets via Key Vault β Workload Identity federation, no PATs in cluster
- KEDA scaling β scale-to-zero when no work, burst on demand
- Azure CLI (
az) with an active subscription kubectlandhelmv3- A GitHub PAT with
repo,issues,workflowscopes
# Set your variables
RESOURCE_GROUP="myapp-rg"
LOCATION="eastus"
CLUSTER_NAME="squad-aks"
ACR_NAME="myappsquadacr" # must be globally unique
# Create resource group
az group create --name $RESOURCE_GROUP --location $LOCATION
# Create AKS cluster (with security features)
az aks create \
--resource-group $RESOURCE_GROUP \
--name $CLUSTER_NAME \
--node-count 1 \
--node-vm-size Standard_D2s_v5 \
--enable-managed-identity \
--enable-addons azure-keyvault-secrets-provider \
--enable-oidc-issuer \
--enable-workload-identity \
--no-ssh-key
# Create container registry
az acr create --resource-group $RESOURCE_GROUP --name $ACR_NAME --sku Basic
# Attach ACR to AKS (so AKS can pull images)
az aks update --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --attach-acr $ACR_NAME
# Get cluster credentials
az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME# Option A: Build in the cloud (no local Docker needed!)
az acr build --registry $ACR_NAME --image squad-ralph:latest --file docker/Dockerfile .
# Option B: Build locally
docker build -f docker/Dockerfile -t $ACR_NAME.azurecr.io/squad-ralph:latest .
docker push $ACR_NAME.azurecr.io/squad-ralph:latest# For development: plain K8s Secret
kubectl create namespace squad
kubectl create secret generic squad-runtime-secrets \
--namespace squad \
--from-literal=GH_TOKEN=ghp_your_token_here
# For production: use Azure Key Vault (see docs/key-vault-setup.md)π‘ Copilot CLI Auth: The
GH_TOKENmust belong to a user with an active GitHub Copilot license. The Agency CLI uses this token for both GitHub API access and Copilot model calls. See docs/copilot-cli-auth.md for details.
helm upgrade --install squad-agents ./helm/squad-agents \
--namespace squad \
--create-namespace \
--set global.acrLoginServer=$ACR_NAME.azurecr.io \
--set global.repository=your-org/your-repo \
--set ralph.image.repository=squad-ralph \
--set ralph.image.tag=latest# Check the CronJob
kubectl get cronjobs -n squad
# Manually trigger a test run
kubectl create job ralph-test --from=cronjob/ralph -n squad
# Check logs
kubectl logs -l job-name=ralph-test -n squad --followsquad-on-aks/
βββ README.md # You are here
βββ helm/
β βββ squad/ # Core Squad chart (coordinator + Ralph Deployment)
β β βββ Chart.yaml
β β βββ values.yaml
β β βββ templates/
β β βββ _helpers.tpl
β β βββ configmap.yaml # squad.config.ts + team/routing ConfigMap
β β βββ deployment.yaml # Ralph Deployment (lightweight alternative)
β β βββ ralph-deployment.yaml # Ralph Deployment (full, with emptyDir scratch)
β β βββ secret.yaml # Optional K8s Secret (use Key Vault in prod)
β β βββ service.yaml # Ralph metrics/health Service
β βββ squad-agents/ # AKS-native chart (Ralph CronJob + Picard Deployment)
β βββ Chart.yaml
β βββ values.yaml # ACR, Key Vault, KEDA, Workload Identity config
β βββ templates/
β βββ _helpers.tpl
β βββ namespace.yaml # Squad namespace with Workload Identity label
β βββ serviceaccount.yaml # Workload Identity ServiceAccount
β βββ rbac.yaml # Agent job spawning permissions
β βββ secret-provider-class.yaml # Key Vault CSI integration
β βββ ralph-cronjob.yaml # Ralph work monitor (CronJob)
β βββ picard-deployment.yaml # Lead agent Deployment + inline KEDA ScaledObject
β βββ picard-scaledobject.yaml # Composite AND KEDA ScaledObject (Tier 2)
βββ keda/
β βββ github-rate-scaler.yaml # TriggerAuthentication for GitHub API
β βββ squad-scaledobject.yaml # Standalone KEDA ScaledObject (3 triggers)
βββ infrastructure/
β βββ aks-automatic-squad.bicep # AKS Automatic cluster + ACR + VNet + Log Analytics
β βββ aks-automatic-squad.bicepparam # Default parameters (dev environment)
βββ docker/
β βββ Dockerfile # Multi-stage: PowerShell 7 + Node.js + gh CLI
βββ scripts/
β βββ ralph-watch.ps1 # Ralph's polling loop
βββ .github/
β βββ workflows/
β βββ deploy.yml # Build β Push β Deploy pipeline
βββ docs/
β βββ what-is-squad.md # Squad framework overview
β βββ deployment-timeline.md # Real deployment log (warts and all)
β βββ key-vault-setup.md # Production secrets guide
β βββ keda-scaling.md # Autoscaling with KEDA
β βββ aks-automatic-vs-standard.md # AKS SKU comparison
β βββ troubleshooting.md # Common issues and fixes
βββ examples/
β βββ values-dev.yaml # Development overrides
β βββ values-prod.yaml # Production overrides
βββ LICENSE
Read this before deploying. These are real issues we hit during our first deployment.
| Issue | What Happens | Fix |
|---|---|---|
K8s label / in repo name |
Helm install fails with "invalid label value" | Chart uses replace "/" "_" β already handled |
| CSI driver without Key Vault | Pod stuck in ContainerCreating forever |
Set global.keyVaultName="" to skip CSI volumes |
| No Docker locally | Can't build image on Azure DevBox/Codespace | Use az acr build for cloud builds |
| Enterprise VM restrictions | AKS create fails with "VM size not allowed" | Check az vm list-skus --location <loc> first |
| Issue | What Happens | Fix |
|---|---|---|
| AKS Automatic needs 16 vCPUs | Creation fails on small/restricted subscriptions | Use AKS Standard with smaller VMs |
| Duplicate env vars in CronJob | K8s warning about hidden definitions | Don't override SQUAD_AGENT_TYPE in ralph.env |
| ACR build uploads entire repo | Build takes 30+ minutes with large repos | Use minimal build context or .dockerignore |
| 1000+ subscriptions | az account list is slow, hard to find right sub |
Use --query filter: az account list --query "[?contains(name,'mysubname')]" |
- Node selectors are disabled by default. Uncomment in
values.yamlwhen you add dedicated node pools. - KEDA is disabled by default. Enable after installing the KEDA add-on:
az aks update --enable-keda - Picard (lead agent) is a Deployment, not a CronJob. It stays running. Disable with
picard.enabled=falsefor cost savings. - GH_TOKEN needs
repo,issues,workflowscopes minimum. For org repos, also needsread:org.
The infrastructure/ directory contains Bicep templates for provisioning a complete AKS Automatic cluster:
# Create resource group + deploy AKS Automatic cluster with ACR, VNet, Log Analytics
az deployment group create \
--resource-group squad-aks-rg \
--template-file infrastructure/aks-automatic-squad.bicep \
--parameters infrastructure/aks-automatic-squad.bicepparamAKS Automatic includes KEDA built-in, managed node pools with autoscaling, and Azure RBAC integration.
Note: AKS Automatic requires 16+ vCPUs quota. If your subscription is limited, use AKS Standard instead (see docs/aks-automatic-vs-standard.md).
KEDA can scale Squad agents based on workload. The keda/ directory has standalone ScaledObjects, and the Helm chart includes inline KEDA support:
| Tier | Trigger | Scaler | Effort |
|---|---|---|---|
| 1. Queue-based | Open GitHub issues with squad labels | Built-in github scaler |
Config only |
| 2. Composite AND | Issue count AND rate-limit headroom | github + metrics-api with scalingModifiers.formula |
Config + metrics exporter |
| 3. Token-based | Copilot token budget remaining | Custom Prometheus exporter | ~30 LOC |
See docs/keda-scaling.md for details.
| Layer | Mechanism |
|---|---|
| Pod identity | Azure Workload Identity (OIDC federation) |
| Secrets | Azure Key Vault via CSI driver (no secrets in YAML) |
| RBAC | Minimal Role: batch/jobs create + pods/logs read |
| Container | Non-root user, dropped capabilities, read-only where possible |
| Network | Private cluster option, no public ingress needed |
| Image | ACR with integrated vulnerability scanning |
Contributions welcome! This project came from a real deployment β if you hit something we didn't document, please open an issue or PR.
MIT β see LICENSE.
Built by deploying AI agents to production with GitHub Copilot CLI. The deployment timeline documents every step, failure, and fix.