
August 17, 2026
9 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
If you need to deploy to AKS with Azure Pipelines reliably, the answer lies in treating your infrastructure as code and your release process as an immutable artifact pipeline. Many teams struggle because they conflate building Docker images with orchestrating Kubernetes manifests, leading to fragile scripts and inconsistent environments. A robust implementation separates these concerns into distinct build and release stages, authenticated via workload identity rather than static credentials.
While my daily work often involves deploying Laravel applications via GitLab CI and Deployer on traditional VPS infrastructure, the principles of atomic deployments and configuration management translate directly to Kubernetes. When moving to managed cloud-native platforms, the complexity shifts from server provisioning to manifest orchestration and identity federation. For teams evaluating whether this level of orchestration is necessary versus simpler platform options, understanding the trade-offs between managed cloud services and traditional hosting helps justify the operational overhead of AKS. The following guide assumes you have already decided on AKS and focuses strictly on the engineering mechanics of doing it correctly in 2026.
How do you securely connect Azure Pipelines to AKS without secrets?
The era of storing Kubernetes admin kubeconfig files or long-lived service principal passwords in Azure DevOps library variables is over. In 2026, the only acceptable method to deploy to AKS with Azure Pipelines is through Microsoft Entra ID Workload Identity Federation. This eliminates credential rotation headaches and reduces the blast radius if a pipeline account is compromised.
Configure the Service Connection
Navigate to Project Settings > Service Connections and create a new "Azure Resource Manager" connection. Select "Workload identity federation (automatic)" if your subscription permits, or manual if you need to pre-create the app registration. Crucially, scope this connection to the specific resource group containing your AKS cluster, not the entire subscription. This follows the principle of least privilege I apply whether configuring Linux servers or cloud IAM roles.
Grant RBAC Permissions on AKS
The federated identity needs explicit permissions inside the cluster. Avoid granting cluster-admin. Instead, create a RoleBinding scoped to your target namespace:
<!-- deploy-role-binding.yaml -->
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: azure-pipelines-deployer
namespace: production-app
subjects:
- kind: User
name: "system:serviceaccount:azdo:pipeline-sa"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: edit
apiGroup: rbac.authorization.k8s.io In production environments, replace the built-in edit ClusterRole with a custom role that only permits updates to Deployments, Services, ConfigMaps, and Secrets within that namespace. This prevents a compromised pipeline from modifying network policies or mutating other tenants.
What is the correct YAML structure for building and pushing to ACR?
A common mistake when engineers first deploy to AKS with Azure Pipelines is combining the image build and the cluster deployment into a single job. This creates tight coupling and makes rollbacks impossible without rebuilding. Always separate these into distinct stages. The build stage produces an immutable artifact (the tagged container image), while the deploy stage consumes it.
Multi-Stage Pipeline Skeleton
This structure mirrors the separation of concerns I use when architecting REST APIs where business logic is decoupled from transport layers. The build stage should never touch the cluster, and the deploy stage should never compile code.
# azure-pipelines.yml
trigger:
branches:
include: [main]
paths:
exclude: [docs/*, README.md]
variables:
acrName: 'myregistry.azurecr.io'
imageName: 'webapp'
tag: '$(Build.BuildId)'
stages:
- stage: Build
displayName: 'Build & Push Image'
jobs:
- job: BuildImage
pool:
vmImage: 'ubuntu-24.04'
steps:
- task: Docker@2
inputs:
command: 'buildAndPush'
repository: $(imageName)
dockerfile: '$(Build.SourcesDirectory)/Dockerfile'
containerRegistry: 'ACR-Service-Connection'
tags: |
$(tag)
latest
- stage: Deploy
displayName: 'Deploy to AKS'
dependsOn: Build
condition: succeeded()
jobs:
- deployment: DeployProd
environment: 'aks-production'
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: KubernetesManifest@1
inputs:
action: 'deploy'
kubernetesServiceConnection: 'AKS-Federated-Conn'
namespace: 'production-app'
manifests: '$(Pipeline.Workspace)/s/k8s/'
imagePullSecrets: 'acr-auth'
containers: '$(acrName)/$(imageName):$(tag)' Optimizing Docker Builds for AKS
When targeting AKS, ensure your Dockerfile uses multi-stage builds to keep images lean. Large images increase pull times across nodes and inflate storage costs. For PHP/Laravel applications, this means separating composer install and npm build stages from the final runtime image. Also, enable ACR artifact cache to speed up subsequent pipeline runs, especially when working with teams distributed across regions where bandwidth can be variable.
Should you use Helm, Kustomize, or raw manifests for AKS deployments?
The choice of templating engine significantly impacts maintainability. Having maintained complex e-commerce systems where configuration drift causes real revenue loss, I prefer tools that enforce declarative consistency over flexible but error-prone templating.
| Criteria | Raw Manifests | Kustomize | Helm |
|---|---|---|---|
| Complexity | Low | Medium | High |
| Environment Variance | Poor (copy-paste) | Excellent (overlays) | Good (values files) |
| Templating Logic | None | Patch-based only | Full Go templates |
| Native Azure Support | KubernetesManifest task | KubernetesManifest task | HelmDeploy task required |
| Best For | Simple apps, learning | Microservices, GitOps | Platform charts, vendors |
Why Kustomize Wins for Custom Applications
For bespoke applications like legal-tech portals or custom e-commerce platforms, Kustomize offers the best balance. It avoids the "template spaghetti" problem where Helm charts become unmaintainable due to excessive conditionals. Kustomize overlays let you maintain a clean base configuration and apply environment-specific patches (replica counts, resource limits, domain names) without duplicating entire manifest files. The KubernetesManifest@1 task natively supports Kustomize via the kustomizationPath input, eliminating extra tooling installation steps.
When Helm Is Unavoidable
Use Helm when deploying third-party infrastructure components (Ingress controllers, cert-manager, monitoring stacks) where upstream charts are the standard distribution format. Never write a custom Helm chart for your application unless you plan to distribute it to multiple unrelated organizations. The cognitive overhead of debugging Go template rendering errors during a production outage is rarely worth the abstraction benefit for internal apps.
How do you implement zero-downtime deployments and safe rollbacks?
Deploying to Kubernetes is not inherently zero-downtime. Without explicit strategy configuration, you risk service interruptions during pod replacement. This matters critically for transactional systems where even seconds of downtime affect user trust and revenue.
Configure Rolling Update Strategy
Always specify update strategy in your Deployment manifest. The default may be acceptable for stateless web apps, but tuning it prevents resource exhaustion on smaller AKS node pools:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Create at most 1 extra pod during update
maxUnavailable: 0 # Never drop below desired replica count
minReadySeconds: 30 # Wait for readiness probe before proceeding Setting maxUnavailable: 0 guarantees capacity is never reduced during deployment. Combined with proper readiness probes (not just liveness probes), this ensures traffic only shifts to new pods once they are fully initialized. For Laravel apps, this includes checking database connectivity and cache availability, not just HTTP 200 on /health.
Automated Rollback Triggers
Azure Pipelines does not automatically rollback failed Kubernetes deployments. You must implement this explicitly. Add a post-deployment validation step that checks pod health and reverts if unhealthy:
- script: |
kubectl rollout status deployment/webapp -n production-app --timeout=300s
if [ $? -ne 0 ]; then
echo "##vso[task.logissue type=error]Deployment failed, initiating rollback"
kubectl rollout undo deployment/webapp -n production-app
exit 1
fi
displayName: 'Validate & Auto-Rollback' This pattern catches issues that pass initial manifest validation but fail at runtime—misconfigured environment variables, missing secrets, or incompatible schema changes. In my experience maintaining high-traffic platforms, automated rollback reduces mean-time-to-recovery from minutes to seconds compared to manual intervention.
Managing Database Migrations Safely
Never run migrations as part of the container entrypoint during deployment. This causes race conditions when multiple pods start simultaneously. Instead, use an init container or a dedicated migration job that runs before the main deployment. For frameworks like Laravel or Symfony, this means running php artisan migrate --force in a controlled, single-execution context. If the migration fails, the pipeline stops before updating the Deployment, preventing new code from running against an old schema.
How do you handle secrets and configuration across environments?
Hardcoding configuration in manifests or pipeline variables is a security anti-pattern. For teams evaluating whether to adopt enterprise-grade practices, understanding fundamental security principles for web infrastructure provides context for why secret management matters beyond compliance checkboxes.
Integrate Azure Key Vault with AKS
Use the Azure Key Vault Provider for Secrets Store CSI Driver. This mounts secrets as volumes rather than environment variables, preventing leakage in logs and crash dumps. Configure it in your pipeline by ensuring the workload identity has Key Vault read permissions, then reference secrets in your pod spec:
volumes:
- name: app-secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "app-kv-secrets"
containers:
- name: webapp
volumeMounts:
- name: app-secrets
mountPath: "/mnt/secrets"
readOnly: true Environment-Specific Configuration Management
Separate structural configuration (ports, paths, feature flags) from sensitive data (API keys, DB passwords). Use ConfigMaps for the former, Key Vault for the latter. When using Kustomize, each overlay references its own ConfigMap generator, ensuring staging never accidentally loads production values. This discipline prevents the "it works on staging" syndrome that plagues teams who share configuration sources across environments.
Conclusion
To successfully deploy to AKS with Azure Pipelines in 2026, prioritize workload identity federation over static credentials, separate build and deploy stages immutably, choose Kustomize for application manifests, and enforce zero-downtime strategies with automated rollback safeguards. These patterns transform Kubernetes deployment from a fragile manual ritual into a reliable, auditable engineering process. If your team needs help implementing these practices or evaluating whether AKS is the right fit versus simpler alternatives, reach out to discuss your specific infrastructure requirements.

