The Kladde has run on Azure Container Apps since K3. It scales with the length of a queue down to zero, it gets its secrets through a managed identity, and a pipeline delivers it without a stored password. Even so, sooner or later the word Kubernetes comes up in almost every conversation about containers: as a job requirement, as a customer's wish, as "that's how it's done these days."
This course deals with that honestly. It teaches you how Kubernetes works, and to do so it builds the Kladde on an AKS cluster. But the first
lesson asks the question that comes before everything else: Does the Kladde need Kubernetes at all? At the end of the lesson the answer stands
in writing in docs/decisions.md, with reasons and with the conditions under which it would tip. What is expected to be written there: The
academy's stack stays Container Apps. AKS is the exercise and the decision aid, not the goal.
What containers, images and a registry are is covered in K1 and in lessons 3.1 and 3.2; Container Apps, revisions and scaling you know from K3. This lesson does not repeat that, it assumes it.
What Kubernetes solves
According to Microsoft's description, Kubernetes is an open-source platform that automates the deployment, scaling and management of containerized applications. That sounds like an advertising line. Concretely, it means three things.
Orchestration. One container runs somewhere. With two containers on two machines you already ask: Which one runs what, where does the traffic go, what happens when a machine fails? Kubernetes answers that for dozens or thousands of containers. It distributes them across nodes (machines), gives them network addresses and names, and makes sure traffic goes only where a container is ready.
Self-healing. If a container crashes, Kubernetes restarts it. If a node fails, it rebuilds the containers on other nodes. If a container no longer answers its health check, it takes it out of rotation. All of this happens without your involvement, but by rules that you wrote down beforehand.
Declarative state. You don't tell Kubernetes what to do ("start three containers"), but what it should look like ("three replicas of this image are running"). The wish is called the desired state, the real state the actual state. In between works a control loop: A controller compares the two, and if the actual state deviates, it acts until both are equal again. That is the idea on which everything else rests. It explains why a deleted pod comes back by itself and why you feed Kubernetes with files instead of commands.
The building blocks in one sentence
According to the AKS documentation, a cluster consists of two parts: the control plane, which holds the state and decides on
placement, and the nodes, the virtual machines on which your applications run. The control plane includes
the API server (every request to the cluster goes through it), etcd (the store of the state), the scheduler (it picks a node for every new pod)
and the controller manager (it runs the control loops). On every node work the kubelet, which makes sure that containers in the
pod run, and a container runtime. A pod is a group of one or more containers that share network and storage.
Lesson W1.2 builds these terms up hands-on.
What Container Apps already manages for you
Container Apps is not an alternative to Kubernetes but a service built on it. The documentation says: Container Apps is powered by Kubernetes and open-source technologies such as Dapr, KEDA and Envoy and supports "Kubernetes-style" applications and microservices with service discovery and traffic splitting. That includes scaling by traffic and by event sources such as queues, down to zero as well, and jobs that start on demand, on a schedule or through events. You used exactly these parts in K3.
What you did not do there: You never saw a node, never chose a node pool, never wrote a Kubernetes manifest. The documentation states this openly: Container Apps gives no direct access to the Kubernetes API. Whoever needs it or the control plane should take AKS. Whoever wants to build "Kubernetes-style" applications without needing all the native interfaces gets, with Container Apps, a fully managed environment based on proven patterns. That is why, according to the documentation, many teams start with Container Apps.
The following table maps what you know from K3 to what Kubernetes calls it. It is a signpost for the next lessons, not an equation: The things are related, not identical.
| Task | In Container Apps (K3) | In Kubernetes (this course) |
|---|---|---|
| running container | replica of a revision | pod |
| desired count and update procedure | revision, minReplicas, maxReplicas |
deployment with replicas and rolling update (W1.2) |
| name and distribution in the network | the app's internal name, ingress | service (W1.2), gateway and route (W1.5) |
| configuration | environment variables, secrets with a Key Vault reference | ConfigMap, Secret, CSI driver for Key Vault (W1.2, W1.3) |
| identity | the app's managed identity | workload identity through a service account (W1.3) |
| scaling | scaling rules, KEDA inside | configure HPA and KEDA yourself (W1.6) |
| delivery | azd, pipeline |
Helm, GitOps with Flux (W1.4, W1.7) |
| maintenance of the platform | Azure's business | shared with AKS Automatic, yours with AKS Standard (below) |
An example makes the difference tangible. The same statement "two replicas of the web app, at least one always reachable" stands in Container Apps as part of the app's Bicep description:
// Container Apps (K3): the platform decides on which node a replica runs; you never see the node.
template: {
containers: [
{ name: 'web', image: webImage, resources: { cpu: json('0.25'), memory: '0.5Gi' } }
]
scale: {
minReplicas: 2
maxReplicas: 4
}
}
In Kubernetes you write it as a deployment. Read the example only for comparison; in lesson W1.2 you build it up line by line:
# Kubernetes (W1.2): you describe the state; the cluster's controllers work towards it.
apiVersion: apps/v1
kind: Deployment
metadata:
name: kladde-web
namespace: kladde
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: kladde-web
template:
metadata:
labels:
app.kubernetes.io/name: kladde-web
spec:
containers:
- name: web
image: acrkladdeabc123.azurecr.io/kladde-web:1.0.0
You see: the same ideas, but in Kubernetes they lie on the surface. That is strength and burden at once. Every setting that Container Apps makes for you, you make yourself here, or you let AKS Automatic make it.
What Container Apps does not offer
"No access to the Kubernetes API" is abstract. Take it as a list of what you cannot do without the API:
- Install third-party software that requires Kubernetes. Many tools are delivered as a Helm chart or as an operator: their own resource types that a controller in the cluster evaluates. Without a cluster there is no place for them.
- GitOps the Kubernetes way. A configuration repository from which a controller in the cluster produces the state (W1.7) presupposes a cluster. Container Apps you know from K3 with pipelines that deliver by push.
- Shaping nodes, network and policies yourself. Your own node pools, your own network policies at layer 7, your own entrances: That is the job of a platform team, and Container Apps takes it off your hands by not giving it to you.
- The same manifest in several places. Kubernetes runs at the big cloud providers and in your own data center. Whoever cares that their description applies there unchanged speaks for Kubernetes. Note the limitation: Manifests are portable, the services around them (identity, registry, queue, entrance) are not. For that the Kladde needs a handful of Azure specifics in W1.3.
Where the line runs exactly changes with the services. So always check a concrete need against the current documentation of Container Apps, not against this list; the checklist below demands evidence for that.
AKS: Automatic and Standard
Azure Kubernetes Service (AKS) is Azure's managed Kubernetes service. Azure runs the control plane in both variants. What differs is who runs the rest. The documentation names two modes:
- AKS Automatic: Azure sets up the cluster and runs nodes, scaling, security defaults and upgrades with defaults following the recommendations of the AKS Well-Architected Framework. It is designed for production applications.
- AKS Standard: You have "maximum flexibility" and direct control over node pools, scaling, network and operations, and you carry them.
The documentation contains a long comparison table. The rows that count for the decision, in your words:
| AKS Automatic | AKS Standard | |
|---|---|---|
| System nodes | preconfigured: Azure creates, scales and updates system nodes and components and hosts them in AKS subscriptions | default: you create and manage system node pools |
| Nodes for your application | preconfigured: automatic provisioning on demand (node auto-provisioning) | optional |
| Scaling | HPA, KEDA and VPA are turned on | by hand, optionally cluster autoscaler, KEDA, VPA |
| Tier and SLA | Standard tier with an uptime SLA for the API server (99.95%) and a pod readiness SLA (99.9% of operations within five minutes) | Free tier as the default; Standard tier optional |
| Upgrades | automatic through the stable channel, to minor version N-1; node images automatic | by hand; automatic optional |
| Sign-in to the cluster | preconfigured: Azure RBAC for Kubernetes | default: local accounts; optionally Azure RBAC |
| Policies | preconfigured: deployment safeguards and pod security standards (Baseline), enforcing | optional, warning or enforcing |
| Workload identity, OIDC issuer | preconfigured | optional |
| Network | managed virtual network with Azure CNI Overlay and Cilium, managed NAT gateway | kubenet by default; everything selectable |
The word "preconfigured" has a precise meaning in the documentation: Preconfigured features are always on, and you can neither turn them off nor change their settings. "Default" means: set, but changeable. Whoever takes AKS Automatic buys security with less room to move. For the Kladde that is a good trade, and it has consequences that you will see in error messages in W1.2 and W1.3.
You should know three limits of AKS Automatic before deciding, because they cannot be fixed later:
- Switching from Standard to Automatic is only a preview and a rebuild. Microsoft describes the migration from the base SKU (Standard) to the Automatic SKU on the page "Migrate AKS Automatic clusters and managed system node pools" (read November 12, 2026) as a preview: no warranty, not meant for production, and only in regions where managed system node pools are generally available. Beforehand you have to rebuild the Standard cluster to match the Automatic configuration (among other things Azure CNI Overlay with Cilium, API Server VNet Integration, safeguards in Enforce mode); some steps restart nodes or cannot be undone. Whoever starts with AKS Standard therefore doesn't plan on switching later and, when in doubt, builds a new cluster.
- Windows nodes are not supported, and neither are the Dapr and Azure Machine Learning extensions.
- The region must offer Automatic.
germanywestcentral, the Kladde's region, is on the list of regions with general availability.
It is best to avoid one mix-up right away: "AKS Standard" is a cluster mode, "Standard" is also the name of a pricing tier of cluster management (Free, Standard, Premium). AKS Automatic runs in the Standard tier. The two have nothing to do with each other.
What it costs, without naming a number
Numbers change, so this lesson contains none. Where the money goes rarely changes:
| Cost item | When it runs | Who influences it |
|---|---|---|
| Nodes | The documentation says: Agent nodes are billed like normal virtual machines. They run as long as the cluster has pods, even without load. | size and number of pods (requests!), node autoscaling |
| Tier of cluster management | per cluster; AKS Automatic runs in the Standard tier | the choice of mode |
| Outbound traffic | AKS Automatic uses a managed NAT gateway | amount of traffic |
| Entrance | as soon as an ingress or gateway with a public address exists (W1.5) | number of gateways |
| Logs and metrics | Container Insights and managed Prometheus are the default in Automatic through Azure CLI and portal | amount of logs |
Container Apps, by contrast, bills by the seconds in which a replica runs, and has a free grant (K3, lesson 3.5.7). An app at zero costs no compute time. A cluster has this "to zero" only for your pods, not for itself. That is the most important cost difference for a small office, and it belongs in the checklist. You read the current price in the pricing calculator.
The checklist
A decision needs questions for which there are answers with evidence. This lesson's checklist has two parts:
- Must-have criteria: needs that only a cluster meets. One "yes" with evidence is enough to examine AKS seriously.
- Counter-criteria: circumstances that speak for Container Apps. They don't decide alone, but they belong in the justification.
The questions stand as data in a file, so that a decision can later be repeated and compared:
{
"mustCriteria": [
{ "id": "M1", "question": "Does the application need the Kubernetes API, for example operators or custom resource types?" },
{ "id": "M2", "question": "Does a customer or a tool demand Helm charts or GitOps in the cluster?" },
{ "id": "M3", "question": "Do we need control over nodes, network policies or entrances that Container Apps does not offer?" },
{ "id": "M4", "question": "Does the same description have to apply in several clouds or in our own data center?" }
],
"counterCriteria": [
{ "id": "G1", "question": "Do we lack a team that runs a platform, that is, watches versions, nodes and policies?" },
{ "id": "G2", "question": "Does the load have long pauses in which nothing would need to run?" },
{ "id": "G3", "question": "Are there few services that Container Apps can already express completely?" }
]
}
The script reads the file and the answers, checks both and prints a verdict. It decides nothing, it makes the decision traceable. What matters is the exit code: It says whether the input was fine, not what came out.
# scripts/aks-decision.ps1 - evaluates the checklist; the exit code reports the input, never the verdict.
[CmdletBinding()]
param(
[string] $Checklist = 'docs/aks-checklist.json',
[string] $Answers = 'docs/aks-answers.json'
)
$ErrorActionPreference = 'Stop'
try {
$list = Get-Content -Raw -LiteralPath $Checklist | ConvertFrom-Json
$answers = Get-Content -Raw -LiteralPath $Answers | ConvertFrom-Json
}
catch {
Write-Error "Input not readable: $($_.Exception.Message)"
exit 2
}
$problems = @()
$yesMust = @()
$yesAgainst = @()
foreach ($group in 'mustCriteria', 'counterCriteria') {
foreach ($item in $list.$group) {
$answer = $answers.($item.id)
if (-not $answer -or $answer.answer -notin 'yes', 'no') {
$problems += "$($item.id): answer must be 'yes' or 'no'."
}
elseif ([string]::IsNullOrWhiteSpace($answer.evidence)) {
$problems += "$($item.id): without evidence, no answer counts."
}
elseif ($answer.answer -eq 'yes') {
if ($group -eq 'mustCriteria') { $yesMust += $item.id } else { $yesAgainst += $item.id }
}
}
}
if ($problems) {
$problems | ForEach-Object { Write-Error $_ -ErrorAction Continue }
exit 2
}
"Must-have criteria with yes: $($yesMust.Count) $($yesMust -join ', ')"
"Counter-criteria with yes: $($yesAgainst.Count) $($yesAgainst -join ', ')"
if ($yesMust.Count -gt 0) {
'Verdict: examine AKS seriously. At least one need can be met only by a cluster.'
}
else {
'Verdict: Container Apps stays. No need calls for a cluster.'
}
exit 0
Two lines in it are deliberately strict. An answer without evidence doesn't count: "Maybe we need Helm" is not evidence, "Customer X requires a Helm chart under contract Y" is. And exit code 2 separates broken input from the verdict, so that a pipeline or a review can tell the two apart. You know the same principle from K3: The exit code counts, never the output alone.
When AKS is the right choice
Turn the checklist around and it describes when AKS wins:
- Your software, or the software you have to run, is made for Kubernetes (operators, charts), and replacing it costs more than a cluster.
- You have a platform team for which version deadlines, nodes and policies are everyday business. AKS supports every GA version for twelve months; whoever stays longer on a version that is no longer supported is updated by the platform itself after a grace period, without a maintenance window and without a stop at deprecated APIs. The date from which this applies is in the documentation of the supported versions; read it there, not in this lesson.
- You need the same description in several places or a capability that Container Apps doesn't have.
- Your operation is big enough that the effort for a cluster spreads out: many services, several teams, a shared foundation.
And if AKS, then Automatic first. The documentation recommends Automatic as the standard path for production applications. You choose Standard only if a default stands in your way that Automatic doesn't let you change: for example Windows nodes, an unsupported extension or the warning level of the policies. Because a later switch is possible only as a preview with a rebuild (see above), the reason for it belongs in the decision.
Reasoning traps
| Reasoning trap | Why it doesn't hold |
|---|---|
| "Everyone uses Kubernetes." | A majority is not a need. Ask for the function you would use tomorrow. |
| "AKS Automatic is serverless." | It takes node maintenance off your hands, but the nodes exist, and they are billed like virtual machines. |
| "With Kubernetes we are independent of Azure." | The manifest is portable, identity, registry, queue and entrance stay Azure. The dependency moves, it doesn't disappear. |
| "Container Apps is Kubernetes light, so a move later is easy." | A move is possible, but it is work: Scaling rules, identities, entrance and delivery have to be rewritten (the rest of the course). |
| "We start with Standard and switch to Automatic later." | The migration exists only as a preview, and it requires rebuilding the cluster first (state of Microsoft's page: November 12, 2026). It isn't meant for production. |
| "The cluster only costs money when it works." | Nodes run as long as pods need room; a cluster without load costs money all the same. |
Exercise 1: Hold the Kladde against the checklist
In your Kladde repository, create three files: docs/aks-checklist.json (the questions from above), scripts/aks-decision.ps1 (the
script from above) and docs/aks-answers.json, in which you answer each of the seven questions for the Kladde, with answer (yes or
no) and an evidence from the repository or the K3 lessons. Run the script and read its exit code.
How to check it: The script ends with exit code 0, names the number of yes answers per group and a verdict. Then remove the evidence from one answer: The script ends with 2 and names the question.
Pause the video and try it before you read on.
Solution 1
One possible docs/aks-answers.json. Every evidence names something checkable. Your answers may turn out differently if your evidence does:
{
"M1": { "answer": "no", "evidence": "Kladde.Web and Kladde.Worker are ordinary containers; no operator, no custom resource types (lesson 3.5)" },
"M2": { "answer": "no", "evidence": "No customer and no tool demands Helm or GitOps; delivery runs with azd and a pipeline (3.7)" },
"M3": { "answer": "no", "evidence": "Entrance through Front Door and Container Apps suffices; no network policies at layer 7 required (3.3.4)" },
"M4": { "answer": "no", "evidence": "The Kladde runs only in Azure; no second provider, no data center" },
"G1": { "answer": "yes", "evidence": "The office has no platform team; versions and nodes would depend on one person" },
"G2": { "answer": "yes", "evidence": "The worker has work only after the nightly fetch and on uploads; in Container Apps it runs at zero (3.5.7)" },
"G3": { "answer": "yes", "evidence": "Two services, web and worker, plus a scheduled job; everything is expressed in Container Apps" }
}
pwsh -NoProfile -File scripts/aks-decision.ps1
$LASTEXITCODE
Must-have criteria with yes: 0
Counter-criteria with yes: 3 G1, G2, G3
Verdict: Container Apps stays. No need calls for a cluster.
0
Now take the evidence out of M1 ("evidence": "") and run the script again: It writes an error of the form M1: without evidence, no answer counts.
and ends with 2. With -File, pwsh passes the script's exit code on; whoever uses -Command gets a 1 back for every
value other than zero. As a check from the other side: Set M2 to yes with the evidence "Customer demands Helm chart". The verdict
switches to "examine AKS seriously". It is a verdict about needs, not about taste.
Exercise 2: Kubernetes in the language of Container Apps
Create docs/aks-comparison.md. Map eight Kubernetes terms to their counterpart in Container Apps, or write "none" and
justify it in one sentence: pod, deployment, service, ConfigMap, namespace, node pool, ingress and Helm chart. Use only what you know from
K3 or have read in this lesson; mark what you don't know with "to check".
How to check it: Every row has either a counterpart with a source (a K3 lesson or the documentation) or "none" with a reason. At least one row is honestly "none".
Solution 2
One possible table. For rows without a counterpart, the reason is given, not a shortcoming of the platform:
| Kubernetes | Container Apps | Note |
|---|---|---|
| Pod | replica | both are the smallest running unit; a pod may carry several containers, and a replica too (K3) |
| Deployment | container app with revisions | revisions keep old states like the ReplicaSets of a deployment; traffic is split per revision |
| Service | the app's internal name and its ingress | service discovery exists in both; a service selects pods by labels, the app has a name |
| ConfigMap | the app's environment variables | nothing confidential; for confidential data a Secret, in Container Apps a secret with a Key Vault reference (3.3.2) |
| Namespace | none | The environment groups apps, but without quotas and access rights per group; to check how far that reaches |
| Node pool | none | You don't see nodes in Container Apps; that is by design, not a shortcoming |
| Ingress | the app's ingress | in AKS: ingress or Gateway API (W1.5) |
| Helm chart | none | A chart presupposes the Kubernetes API; in Container Apps, Bicep describes the app |
The honest "none" rows are the most interesting ones: That is where what you gain with AKS lies, and have to run yourself.
Exercise 3: Write the decision
Write the entry "Container Apps or AKS" into docs/decisions.md, which you have kept since K2. It has four parts: the decision in
one sentence; the reasons, each with a reference to an answer of the checklist; the tipping conditions, that is, what would have to change for the
decision to become wrong; and what the course does with AKS anyway. Check that every tipping condition has a test that you can run.
How to check it: A reader who doesn't know the checklist understands from the entry alone what applies, why, and how you notice that it no longer applies.
Solution 3
One possible entry. You set the date yourself:
## Container Apps or AKS
As of: <date> · Decided by: operations of the Kladde · Review: with every change in the tipping conditions
Decision: The Kladde keeps running on Azure Container Apps. We run AKS only as an exercise (course W1) and delete the cluster afterwards.
Why:
- No must-have criterion applies (docs/aks-answers.json, M1 to M4, each with evidence).
- We have no platform team (G1): version deadlines, nodes and policies of a cluster would depend on one person.
- The worker has long pauses (G2). Container Apps scales it to zero and bills by the seconds of a running replica;
a cluster has nodes that keep running without load.
- Two services and a job (G3) are fully described in Container Apps; a cluster would be more platform than application.
What would tip the decision (each checkable):
- A customer contractually demands a Helm chart or GitOps (test: the contract clause is on file; docs/aks-answers.json M2 becomes "yes").
- We need software that is delivered as an operator (M1).
- The Kladde is to run in a second data center (M4).
- We hire someone who runs the platform (G1 becomes "no").
Then: move to AKS Automatic. AKS Standard only with a written reason, because a later switch is possible only as a preview and with a rebuild.
What we do anyway: Course W1 builds the Kladde on an AKS Automatic cluster in the region germanywestcentral, each time in
one sitting and with a budget alert beforehand and deleting the resource group afterwards.
Two small things are deliberate. The tipping conditions are statements about the world that someone can check; "if we have more load" would not be one, because it doesn't say how much. And the entry doesn't exempt the course: It says openly that W1 is an exercise and not an order to move.
The Kladde after this lesson
docs/aks-checklist.json,docs/aks-answers.jsonandscripts/aks-decision.ps1: a checklist with evidence that can be repeated.docs/aks-comparison.md: the terms of Kubernetes and Container Apps side by side.- An entry "Container Apps or AKS" in
docs/decisions.mdwith tipping conditions. - No new resource in Azure and no cluster: This lesson costs nothing.
What you can do now
- You explain orchestration, self-healing and the declarative desired-versus-actual comparison as a control loop.
- You name what Container Apps already manages for you from Kubernetes, and what is missing without access to the Kubernetes API.
- You tell AKS Automatic and AKS Standard apart by responsibility and limits and know that a later switch is possible only as a preview and with a rebuild.
- You apply a checklist with evidence and evaluate it with a script whose exit code reports the input.
- You write down a decision with checkable tipping conditions.
Further reading
- Comparing Container Apps with other Azure container options: when Container Apps, AKS and the other services fit.
- What is Azure Kubernetes Service (AKS) Automatic?: what is preconfigured, with the full comparison table.
- Core concepts for Azure Kubernetes Service (AKS): control plane, nodes, pods, namespaces and pricing tiers.
- Supported Kubernetes versions in AKS: version deadlines and platform-driven upgrades.