KubeBolt docs
GitHub

Azure AKS Guide

Deploy KubeBolt on Azure Kubernetes Service, including AGIC ingress and Azure Monitor managed Prometheus.

Quick install

The chart works out of the box on AKS — it creates a ServiceAccount and a ClusterRole granting KubeBolt read access to the cluster plus the write verbs its actions use (exec, port-forward, scale, restart, edit, delete).

helm install kubebolt oci://ghcr.io/clm-cloud-solutions/kubebolt/helm/kubebolt \
  --namespace kubebolt --create-namespace --wait

kubectl -n kubebolt get secret kubebolt-admin-password \
  -o jsonpath='{.data.password}' | base64 -d ; echo

kubectl -n kubebolt port-forward svc/kubebolt 3000:80

Open http://localhost:3000 and log in as admin with the printed password (other ways to get it: Quick Start).

AKS enables Metrics Server by default. Confirm with kubectl get apiservice v1beta1.metrics.k8s.io.

Authentication

KubeBolt itself needs no Azure permissions — it talks to the Kubernetes API and nothing else. Workload Identity matters only when the ServiceAccount also has to reach an Azure service, which for KubeBolt means one case: reading Azure Monitor managed Prometheus with the agent, below.

The chart can annotate KubeBolt’s ServiceAccount if your platform conventions require it — --set serviceAccount.annotations."azure\.workload\.identity/client-id"=<CLIENT_ID> — but that alone does not federate anything, and KubeBolt has no Azure call to make. The full Workload Identity chain is set up once, for the agent, in Azure Monitor managed Prometheus below.

Azure RBAC for Kubernetes. If the cluster authorizes through Azure RBAC instead of native Kubernetes RBAC, the chart’s ClusterRole still applies — the two coexist, and the chart’s ServiceAccount uses native Kubernetes RBAC.

Networking. Both kubenet and Azure CNI work with no extra configuration.

Ingress

With the Application Gateway Ingress Controller:

helm install kubebolt \
  oci://ghcr.io/clm-cloud-solutions/kubebolt/helm/kubebolt \
  --namespace kubebolt --create-namespace \
  --set ingress.enabled=true \
  --set ingress.className=azure-application-gateway \
  --set ingress.hosts[0].host=kubebolt.example.com \
  --set ingress.hosts[0].paths[0].path=/ \
  --set ingress.hosts[0].paths[0].pathType=Prefix

With ingress-nginx, which is common on AKS, swap the class:

  --set ingress.className=nginx

The chart also supports the Gateway API instead of an Ingress — --set gatewayAPI.enabled=true, mutually exclusive with ingress.

Azure Monitor managed Prometheus

If your metrics already live in an Azure Monitor Workspace, deploy the optional agent in promread mode and it reads the metric families straight from the workspace, authenticating with Azure Workload Identity — no second scrape pipeline and no secret to rotate.

The cluster needs three things enabled: --enable-oidc-issuer, --enable-workload-identity, and --enable-azure-monitor-metrics (which provisions the workspace and the ama-metrics scrapers).

1. Find the endpoint

Endpoints are workspace- and region-scoped, with a random suffix Azure picks at provisioning time, so look it up rather than guessing:

AMW_ID=$(az resource list \
  --resource-type "Microsoft.Monitor/accounts" \
  --query "[0].id" -o tsv)

AMW_ENDPOINT=$(az resource show --ids $AMW_ID \
  --query "properties.metrics.prometheusQueryEndpoint" -o tsv)

TOKEN=$(az account get-access-token \
  --resource "https://prometheus.monitor.azure.com" \
  --query accessToken -o tsv)

curl -s -H "Authorization: Bearer $TOKEN" \
  "${AMW_ENDPOINT}/api/v1/query?query=count(up)"

A status: success response means the endpoint and the bearer-auth path work.

2. Create the managed identity and the federated credential

Azure needs the most wiring of the three clouds, because each piece lives in a different control plane:

RG=<your-aks-resource-group>
CLUSTER=<your-aks-cluster-name>
LOCATION=eastus
NAMESPACE=kubebolt
KSA=kubebolt-agent   # the chart's default; override with serviceAccount.name
UAMI=kubebolt-promread

# 1. The user-assigned managed identity.
az identity create --name "$UAMI" --resource-group "$RG" --location "$LOCATION"

UAMI_CLIENT_ID=$(az identity show -g "$RG" -n "$UAMI" --query clientId -o tsv)
UAMI_PRINCIPAL_ID=$(az identity show -g "$RG" -n "$UAMI" --query principalId -o tsv)

# 2. Read-only access to the workspace.
az role assignment create \
  --assignee-object-id "$UAMI_PRINCIPAL_ID" \
  --assignee-principal-type ServicePrincipal \
  --role "Monitoring Data Reader" \
  --scope "$AMW_ID"

# 3. Trust the cluster's OIDC issuer for that ServiceAccount.
OIDC_ISSUER=$(az aks show -g "$RG" -n "$CLUSTER" \
  --query "oidcIssuerProfile.issuerUrl" -o tsv)

az identity federated-credential create \
  --name "${UAMI}-fed" \
  --identity-name "$UAMI" \
  --resource-group "$RG" \
  --issuer "$OIDC_ISSUER" \
  --subject "system:serviceaccount:${NAMESPACE}:${KSA}" \
  --audience api://AzureADTokenExchange

Verify before moving on — a silent mis-wire here surfaces as a cryptic AZURE_FEDERATED_TOKEN_FILE not set at runtime:

az role assignment list --assignee "$UAMI_PRINCIPAL_ID" --scope "$AMW_ID" \
  --query "[].roleDefinitionName" -o tsv          # → Monitoring Data Reader

az identity federated-credential show \
  --name "${UAMI}-fed" --identity-name "$UAMI" -g "$RG" \
  --query "{subject:subject,issuer:issuer,audiences:audiences}"

3. Install the agent in promread mode

helm upgrade --install kubebolt-agent \
  oci://ghcr.io/clm-cloud-solutions/kubebolt/helm/kubebolt-agent \
  -n kubebolt --create-namespace \
  --set backendUrl=<your-kubebolt-host>:443 \
  --set tls.enabled=true \
  --set cluster.name=<your-cluster-name> \
  --set auth.mode=ingest-token \
  --set auth.ingestToken.existingSecret=kubebolt-ingest-token \
  --set scrape.enabled=false \
  --set agent.promRead.enabled=true \
  --set agent.promRead.url="${AMW_ENDPOINT}" \
  --set agent.promRead.auth.mode=azureWorkloadIdentity \
  --set serviceAccount.annotations."azure\.workload\.identity/client-id"="${UAMI_CLIENT_ID}" \
  --set-string podLabels."azure\.workload\.identity/use"="true"

Notes that catch people out:

The Add cluster wizard generates this command for you — see Connecting clusters and Metrics.

Troubleshooting

API pod in CrashLoopBackOff. Check kubectl -n kubebolt logs -l app.kubernetes.io/component=api. The usual cause is a missing ClusterRoleBinding — verify with kubectl get clusterrolebinding | grep kubebolt.

Image pull errors behind a private ACR. Allow ghcr.io, or mirror the images and override api.image.repository and web.image.repository (and metrics.storage.embedded.image.repository for VictoriaMetrics) in your values:

az acr import --name myacr --source ghcr.io/clm-cloud-solutions/kubebolt/api:<tag>
az acr import --name myacr --source ghcr.io/clm-cloud-solutions/kubebolt/web:<tag>

403 on some resources. Expected, and handled: KubeBolt degrades gracefully. Restricted resources are dimmed in the sidebar behind a “Limited access” banner. See RBAC & Permissions.