Azure Container Apps vs App Service vs AKS by ops burden
For developers choosing where to run web apps and APIs on Azure, this guide compares Azure Container Apps, App Service, and AKS by who has to operate what day 2. You’ll get a concrete mental model, a realistic deployment path, and decision criteria based on scaling, networking, debugging, and on-call load rather than brochure features.
TL;DR — If your team wants to ship containers without becoming a Kubernetes platform team, start with Azure Container Apps. Use App Service when your app fits the opinionated web-app model and you want the lowest day-2 burden; use AKS only when you explicitly need Kubernetes control and are willing to own node pools, ingress, upgrades, and cluster debugging. Reading time: ~7 min
What it is and where it sits
The useful way to choose between Azure Container Apps, App Service, and AKS is not "which has feature X". It is "which layer of the stack do we want to operate ourselves?"
- App Service sits highest. You hand over code or a container and accept a more opinionated runtime model. You operate the app, app settings, deployment slots, and some networking choices. Azure operates the underlying app hosting fabric.
- Azure Container Apps sits in the middle. You package a container and get container-native deployment, revisions, autoscaling, and HTTP ingress without running a Kubernetes cluster yourself. You operate the container image, runtime config, scaling rules, and environment-level networking.
- AKS sits lowest of the three. You get Kubernetes API control. You also inherit Kubernetes operational work: cluster upgrades, ingress controller choices, node pool sizing, CNI implications, pod disruption budgets, and troubleshooting at the node/control-plane boundary.
In a typical request flow, they all sit behind DNS and TLS, in front of your app process, and beside your data services. The difference is how many layers you own between "incoming HTTPS request" and "container process received bytes on port 8080".
Client
|
| HTTPS
v
DNS / WAF / CDN / Front Door / Application Gateway
|
v
[ App Service ] OR [ Azure Container Apps ] OR [ AKS ingress/service ]
|
v
App process / container
|
+--> Postgres / SQL / Redis / Storage / Event bus
|
+--> Identity provider / external APIs
What each one tends to replace:
- App Service often replaces "VM running IIS/nginx + app runtime" for conventional web apps.
- Container Apps often replaces "small self-managed Kubernetes cluster" for APIs, background workers, and event-driven containers.
- AKS replaces "someone else’s platform choices" when you need Kubernetes primitives directly: custom controllers, service mesh, daemonsets, GPU scheduling, or strict portability around Kubernetes manifests/Helm.
How it actually works
Take one realistic example: a small product team runs a public API in a container, backed by Postgres and Redis. Traffic is bursty. They want blue/green-ish rollouts, autoscaling on HTTP load, private access to data services, and they do not want to spend sprint time on cluster operations.
Step-by-step through Azure Container Apps
- Build and push an image to a registry.
- Deploy the container into a Container Apps environment with ingress enabled.
- Expose port 8080 and set min/max replicas.
- Route traffic to the latest revision, or split traffic between revisions during rollout.
- Scale based on concurrent requests or other rules.
- Connect to Postgres/Redis over private networking if your environment is set up for it.
- Observe logs from the app and platform without touching nodes.
Concrete deployment shape:
az containerapp up \
--name api-prod \
--resource-group rg-prod \
--environment cae-prod \
--image myregistry.azurecr.io/api:2026-08-10.1 \
--target-port 8080 \
--ingress external \
--min-replicas 2 \
--max-replicas 10 \
--env-vars ASPNETCORE_URLS=http://+:8080 LOG_LEVEL=Information
What happens next:
- The platform creates or updates a revision for that image/config combination.
- Incoming HTTPS hits the managed ingress layer.
- Requests are forwarded to healthy replicas of your container.
- If concurrency rises, the platform adds replicas up to your cap.
- If a new revision is unhealthy, traffic can stay on the old one instead of taking down the service.
The failure modes are usually app/container-level, not cluster-level. Example: wrong listening port.
az containerapp logs show \
--name api-prod \
--resource-group rg-prod \
--follow
Typical output shape when the app listens on 3000 but ingress targets 8080:
{"TimeStamp":"2026-08-10T09:14:22.118Z","Log":"Starting container 'api-prod'"}
{"TimeStamp":"2026-08-10T09:14:24.441Z","Log":"Server listening on http://0.0.0.0:3000"}
{"TimeStamp":"2026-08-10T09:14:31.005Z","Log":"Probe failed: dial tcp 10.42.1.17:8080: connect: connection refused"}
{"TimeStamp":"2026-08-10T09:14:31.006Z","Log":"Container 'api-prod' terminated with exit code 1"}
That is a very different operational problem from AKS, where the same symptom might require checking kubectl describe pod, readiness probes, service selectors, ingress annotations, and maybe node-level networking.
Now compare the same app on the other two options:
- App Service: easiest if your app fits its model. You deploy code or a container, set app settings, bind a custom domain, and let the platform handle the rest. Great for web apps and APIs that do not need Kubernetes-style composition. Less flexible if you want multi-container patterns or platform-level event scaling.
- AKS: same app means image, deployment, service, ingress, HPA, secrets, namespace policy, cluster upgrades, and probably an ingress controller. You gain control, but every control surface becomes an operational surface.
When to use it (and when not to)
Choose by the amount of infrastructure your team is willing to understand at 02:00 during an incident.
| Scenario | Recommendation |
|---|---|
| Single web app or API, standard runtime, minimal ops team | App Service |
| Containerized API/worker, autoscaling, revisions, no desire to run Kubernetes | Azure Container Apps |
| Multiple services but team is small and wants container-native deploys without cluster admin work | Azure Container Apps |
| Need Kubernetes APIs directly: Helm-heavy platform, operators, daemonsets, custom ingress, service mesh, GPU workloads | AKS |
| Existing strong Kubernetes platform team already operating clusters well | AKS |
| Legacy app that assumes App Service features/workflows and does not benefit from containers | App Service |
| You want to learn Kubernetes in production by starting with a customer-facing app | Probably not AKS |
You probably don’t need AKS if...
- Your reason is "we might need Kubernetes later."
- You have fewer than a handful of services and no platform engineer.
- Your app is just HTTP + background jobs + database.
- Your team cannot explain how they will handle cluster upgrades, ingress, secret rotation, and workload identity today.
You probably don’t need Container Apps if...
- Your app is a straightforward web app and App Service already fits your deployment/runtime expectations.
- Your team does not want to build and manage container images.
You probably don’t want App Service if...
- Your architecture is already container-first and you want revision-based traffic shifting and container-native scaling behavior.
- You need Kubernetes-specific constructs rather than just "run my app".
Trade-offs
App Service
- Benefit: lowest operational burden for conventional web apps.
Cost: more opinionated runtime and deployment model; less control over container orchestration behavior. - Benefit: simple diagnostics for app-level issues.
Cost: if you need advanced sidecars, Kubernetes-native patterns, or custom scheduling, you hit the edges quickly. - Benefit: fast team onboarding.
Cost: portability is lower if your deployment process leans on App Service-specific assumptions.
Azure Container Apps
- Benefit: container-native deployment without cluster ownership.
Cost: you still own container build hygiene, startup behavior, ports, health, and image supply chain. - Benefit: easier scaling/revisions than raw Kubernetes for small teams.
Cost: less control than AKS over networking internals, add-ons, and Kubernetes ecosystem tooling. - Benefit: good middle ground for APIs, workers, and event-driven services.
Cost: debugging can still involve platform-specific concepts like revisions, ingress mode, and environment networking.
AKS
- Benefit: maximum control and Kubernetes ecosystem compatibility.
Cost: highest operational burden by far: upgrades, node pools, CNI/IP planning, ingress controller lifecycle, policy, quotas, and cluster observability. - Benefit: best fit for sophisticated platform engineering.
Cost: expensive in engineering time even before cloud spend; the hidden bill is on-call complexity. - Benefit: strongest portability story if you already standardize on Kubernetes.
Cost: portability is often overstated if you also depend on cloud load balancers, identity, storage classes, and provider-specific ingress annotations.
A practical rule: if your team would page the application developers to debug node pressure, CNI exhaustion, or ingress-controller upgrades, you are not choosing AKS for technical reasons; you are choosing it to inherit operational work.
In practice
Example 1: Deploy a containerized API to Azure Container Apps
az containerapp create \
--name orders-api \
--resource-group rg-prod \
--environment cae-prod \
--image myregistry.azurecr.io/orders-api:1.4.7 \
--ingress external \
--target-port 8080 \
--cpu 0.5 \
--memory 1.0Gi \
--min-replicas 1 \
--max-replicas 5 \
--env-vars ASPNETCORE_URLS=http://+:8080 ConnectionStrings__Db='Host=postgres.internal;Port=5432;Database=app;Username=appuser;Password=REDACTED'
This creates the app and exposes HTTPS ingress to your container on port 8080. The gotcha: if your process binds only to localhost or the wrong port, health checks fail and you will see repeated restarts rather than a clean HTTP error.
Check the endpoint behavior:
curl -I https://orders-api.example.com/healthz
Healthy output shape:
HTTP/2 200
content-type: text/plain; charset=utf-8
date: Mon, 10 Aug 2026 09:28:11 GMT
server: envoy
Misconfigured HTTP-to-HTTPS or app redirect loop often looks like this:
HTTP/2 307
location: https://orders-api.example.com/healthz
server: envoy
If curl -IL shows repeated 307 or 308, your app is probably forcing HTTPS while the platform already terminated TLS and forwarded the request with headers your framework is not trusting. Fix the framework’s forwarded-header/proxy config rather than adding more redirects.
Example 2: The AKS version of the same app
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-api
spec:
replicas: 2
selector:
matchLabels:
app: orders-api
template:
metadata:
labels:
app: orders-api
spec:
containers:
- name: orders-api
image: myregistry.azurecr.io/orders-api:1.4.7
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: orders-api
spec:
selector:
app: orders-api
ports:
- port: 80
targetPort: 8080
This is only the beginning on AKS: you still need ingress, TLS, DNS, autoscaling, secret handling, and cluster lifecycle. The gotcha: when traffic fails, you now have at least four layers to inspect—pod, service, ingress, and node/network—before you even reach app code.
Useful diagnostics:
kubectl get pods -l app=orders-api
kubectl describe pod -l app=orders-api
kubectl get svc orders-api -o wide
kubectl get ingress
Typical readiness failure shape:
Warning Unhealthy 2m14s (x18 over 5m) kubelet Readiness probe failed: Get "http://10.244.3.27:8080/healthz": dial tcp 10.244.3.27:8080: connect: connection refused
That message is not hard by itself. The burden comes from how many adjacent systems can produce it.
⚠️ Moving an existing production app from App Service or Container Apps to AKS can cause downtime if DNS, TLS certificates, health probes, or session handling differ. Lower your DNS TTL before cutover, test with a temporary hostname first, and only switch traffic after
curl -I, synthetic checks, and application logs all agree.
Example 3: App Service container deployment shape
az webapp config container set \
--name orders-web \
--resource-group rg-prod \
--container-image-name myregistry.azurecr.io/orders-web:2.1.0
This points an App Service app at a container image. The gotcha: the app still lives in App Service’s hosting model, so do not assume Kubernetes-like revisioning, sidecars, or ingress behavior just because the payload is a container.
Further reading
- Azure Architecture Center: Choose an Azure compute service
- Azure Container Apps documentation: Revisions and traffic splitting
- Azure App Service documentation: Configure a custom container
- AKS documentation: Cluster upgrades and node pools
- Kubernetes documentation: Ingress, Services, and Probes
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI