Hitesh Sahu
Hitesh SahuHitesh Sahu
  1. Home
  2. ›
  3. posts
  4. ›
  5. …

  6. ›
  7. 2 1 API Server

Loading ⏳
Fetching content, this won’t take long…


💡 Did you know?

🦥 Sloths can hold their breath longer than dolphins 🐬.

🍪 This website uses cookies

No personal data is stored on our servers however third party tools Google Analytics cookies to measure traffic and improve your website experience. Learn more

Loading ⏳
Fetching content, this won’t take long…


💡 Did you know?

🦥 Sloths can hold their breath longer than dolphins 🐬.
kubernetes

    AI-AgenticAI

    AI-DeepLearning

    AI-GenAI

    AI-Infrastructure

    AI-Machine-Learning

    AI-Math

    AWS

    Azure

    Hobbies

    kubernetes
    • Kubernetes and Cloud Native Certification Path

    • Kubernetes: Control Loops, Scheduling, and GPUs

    • Kubernetes API Server Internals

    • etcd Architecture Explained

    • Kubernetes Scheduler Internals

    • Kubernetes Informers & Controllers Explained

    • Kubernetes Networking: Pods, Services, Ingress, and CNI

    • Kubernetes Storage: PV, PVC, StorageClass, and CSI

    • Helm: Kubernetes Package Manager

    • Cloud Native Observability: Prometheus, Grafana, OpenTelemetry, and Tracing

    • GPU Scheduling in Kubernetes: Device Plugins, GPU Operator & MIG

    • NVIDIA Network Operator: InfiniBand, SR-IOV, RDMA, and Multus

    • Dynamic Resource Allocation: The Future of GPU Scheduling in Kubernetes

    • Kubernetes Performance at Scale

    • Optimizing AI Inference at Scale: The Full Stack

    • Kueue: Kubernetes-Native Job Queuing and Quota Management

    • Multi-Node Distributed Training on Kubernetes

    • Kubernetes Topology Manager: NUMA-Aware GPU Scheduling

    • NVIDIA NIM: Optimized Inference Microservices on Kubernetes

    • GPU Autoscaling on Kubernetes: KEDA, HPA, and Cluster Autoscaler

    • kubernetes Index


    Management

    Programming

    Terraform

    Z_Appendix

    0-root

Cover Image for Kubernetes API Server Internals
kubernetes

Kubernetes API Server Internals

Deep dive into the Kubernetes API Server — authentication, authorization, RBAC, admission controllers, schema validation, the watch cache, optimistic concurrency, and API Priority & Fairness explained with diagrams.

Kubernetes
API Server
Control Plane
Security
RBAC
DevOps
← Previous

Optimizing AI Inference at Scale: The Full Stack

Next →

etcd Architecture Explained

Kubernetes API Server Internals

The API Server (kube-apiserver) is the heart of Kubernetes.

Every operation goes through it — without exception.

Whether you're using kubectl, Helm, ArgoCD, Terraform, or you're a Controller, Scheduler, or Kubelet — you talk to the API Server.

flowchart LR

User["kubectl / Client"]

-->APIServer["API Server"]

APIServer

-->etcd

APIServer

-->Scheduler

APIServer

-->Controller

APIServer

-->Kubelet

Notice: nothing talks directly to etcd. Everything goes through the API Server.


Why Everything Goes Through the API Server

Imagine a bank.

Customers don't walk into the vault.

Customer → Bank Teller → Vault

The API Server is Kubernetes' bank teller. It:

  • Authenticates every caller
  • Authorizes every action
  • Validates every object
  • Persists state to etcd
  • Notifies every watcher of changes

Direct etcd access would bypass all of this — no auth, no validation, no audit trail.


The Request Lifecycle

Every request follows the same six-stage pipeline.

flowchart LR

Request

-->Authentication

Authentication

-->Authorization

Authorization

-->AdmissionControllers["Admission\nControllers"]

AdmissionControllers

-->Validation

Validation

-->Storage

Storage

-->WatchNotification["Watch\nNotification"]

WatchNotification

-->Response

Step 1: Authentication

The API Server first asks: Who are you?

Method How it works
Client Certificates TLS cert signed by cluster CA — used by kubelets, controllers
Bearer Tokens Service Account JWT tokens — used by pods
OIDC Federated identity (Google, Dex, Keycloak) — used for human users
Webhook External auth service called per request

Every kubectl command sends your kubeconfig certificate. The API Server verifies it against the cluster CA.

If authentication fails → 401 Unauthorized. The request stops here.


Step 2: Authorization

Authentication answers who you are.

Authorization answers: what are you allowed to do?

flowchart LR

Request["alice\nPOST /api/v1/pods"]

-->RBAC["RBAC check\nCan alice create pods\nin namespace prod?"]

RBAC

-->|"Role binding found"| Allow["Allow → continue"]

RBAC

-->|"No binding"| Deny["403 Forbidden"]

RBAC

Role-Based Access Control is the default and most common authorization mode.

It has four objects:

Object Scope Purpose
Role Namespace Grants permissions within one namespace
ClusterRole Cluster Grants permissions cluster-wide
RoleBinding Namespace Binds a Role to a user/group/service account
ClusterRoleBinding Cluster Binds a ClusterRole cluster-wide

Example — a developer who can only view pods in production:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: alice-pod-reader
  namespace: production
subjects:
- kind: User
  name: alice
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Now alice can kubectl get pods -n production but cannot delete, create, or access other namespaces.

Other authorization modes: Node (kubelets authorizing node-scoped requests), Webhook (external policy engine), ABAC (attribute-based, rarely used).


Step 3: Admission Controllers

After authorization, admission controllers run.

Authentication says: You're Alice.

Authorization says: Alice may create Pods.

Admission controllers ask: should this specific Pod be modified or rejected?

They act as middleware — running after auth but before persistence.

flowchart LR

Request

-->MutatingAdmission["Mutating\nAdmission"]

MutatingAdmission

-->ValidatingAdmission["Validating\nAdmission"]

ValidatingAdmission

-->Store

Mutating Admission

Mutating webhooks can modify the incoming object before it is stored.

User submits:

containers:
- name: app
  image: nginx

A mutating controller automatically injects:

containers:
- name: app
  image: nginx
  resources:
    requests:
      cpu: "100m"
      memory: "128Mi"
  securityContext:
    runAsNonRoot: true

No user action required. Common examples: Istio sidecar injection, default resource requests, image pull policy enforcement.

Validating Admission

Validating webhooks can only accept or reject — they cannot modify.

securityContext:
  privileged: true

A policy (Kyverno, OPA Gatekeeper, Pod Security Admission) checks this and rejects the request.

403 Forbidden: pods "my-pod" is forbidden: privileged containers are not allowed

Mutating always runs before validating — so a mutating webhook cannot sneak past a validating one by injecting a disallowed field.


Step 4: Schema Validation

After admission, Kubernetes validates the object against its OpenAPI schema.

This is distinct from admission — it checks structural correctness, not policy.

replicas: -5       # rejected — must be >= 0
apiVersion: apps/v10  # rejected — unknown API version
containerPort: "80"   # rejected — must be integer, not string

If schema validation fails → 422 Unprocessable Entity.


Step 5: Persist to etcd

Only now is the object written.

sequenceDiagram

participant Client
participant APIServer as "API Server"
participant Admission
participant etcd

Client->>APIServer: Create Pod

APIServer->>Admission: Mutate + Validate

Admission-->>APIServer: OK

APIServer->>etcd: Store Object

etcd-->>APIServer: Success (resourceVersion: 12345)

APIServer-->>Client: 201 Created

ResourceVersion and Optimistic Concurrency

Every object returned by the API Server has a resourceVersion field:

metadata:
  name: my-pod
  resourceVersion: "12345"

This is a monotonically increasing revision number stored in etcd.

If two clients try to update the same object simultaneously, only the first one wins:

sequenceDiagram

participant ClientA
participant ClientB
participant APIServer as "API Server"

ClientA->>APIServer: GET pod (resourceVersion=12345)
ClientB->>APIServer: GET pod (resourceVersion=12345)

ClientA->>APIServer: PUT pod (resourceVersion=12345) ← arrives first
APIServer-->>ClientA: 200 OK (new resourceVersion=12346)

ClientB->>APIServer: PUT pod (resourceVersion=12345) ← stale
APIServer-->>ClientB: 409 Conflict — resourceVersion mismatch

Client B must re-fetch and retry. This is optimistic concurrency — no locks held, conflicts detected at write time.

Controllers rely on this to safely reconcile without distributed locks.


Step 6: Watch Cache and Fan-Out

After writing to etcd, the API Server notifies every interested watcher.

What is the Watch Cache?

The API Server maintains an in-memory ring buffer of recent events, keyed by resource type and namespace. Clients open long-lived HTTP/2 watch connections and receive events as they happen.

flowchart LR

etcd

-->WatchCache["Watch Cache\n(in-memory ring buffer)"]

WatchCache

-->Scheduler

WatchCache

-->Controller

WatchCache

-->Kubelet

WatchCache

-->Operators

The API Server watches etcd once. Everyone else watches the API Server.

Why Not Watch etcd Directly?

With 10,000 nodes:

10,000 Kubelets + 50 Controllers + 20 Operators + Schedulers = ~10,100 watchers

If all of them opened watch connections to etcd, the load would be crushing.

The Watch Cache acts as a fan-out multiplexer — one etcd watch, thousands of API Server watches.

List + Watch

The standard pattern every controller and kubelet uses:

  1. LIST — fetch all current objects and store them in local cache
  2. WATCH — subscribe to future events from the Watch Cache
  3. From that point on, the local cache stays current via events — no more full lists

This is why a controller restart is fast: it lists once, then tails events. It doesn't re-read etcd from scratch on every decision.


API Priority & Fairness (APF)

At scale, the API Server can be overwhelmed by a flood of requests from one source.

APF classifies every incoming request into a PriorityLevelConfiguration and routes it to a flow queue.

flowchart LR

Requests

-->Classify["FlowSchema\nclassification"]

Classify

-->High["exempt\n(system:masters)"]

Classify

-->Med["workload-high\n(system controllers)"]

Classify

-->Low["global-default\n(user requests)"]

High & Med & Low

-->Workers["API Server\nworker goroutines"]

Built-in priority levels (ordered highest to lowest):

Level Who uses it Can be queued?
exempt system:masters, health checks No — always passes through
node-high Kubelet node status updates Yes
system System controllers (deployment, replicaset) Yes
leader-election Controller leader election Yes
workload-high Authenticated service accounts Yes
workload-low kubectl user requests Yes
global-default Everything else Yes

This ensures a kubectl get pods flood from a CI system cannot starve the Deployment Controller or kubelet heartbeats.

APF is configured via two CRDs: FlowSchema (matches requests to a priority level) and PriorityLevelConfiguration (sets concurrency limits and queue depth per level).


Complete Request Flow

flowchart TB

Request

-->Authentication

Authentication

-->Authorization

Authorization

-->MutatingAdmission["Mutating Admission\n(inject defaults, sidecars)"]

MutatingAdmission

-->ValidatingAdmission["Validating Admission\n(policy check)"]

ValidatingAdmission

-->SchemaValidation["Schema Validation\n(OpenAPI)"]

SchemaValidation

-->etcd["etcd\n(persist + resourceVersion)"]

etcd

-->WatchCache["Watch Cache"]

WatchCache

-->Controllers & Scheduler & Kubelet

Key Takeaways

Stage What it does Failure code
Authentication Verifies caller identity 401
Authorization (RBAC) Checks verb/resource/namespace permissions 403
Mutating Admission Injects defaults, sidecars, labels 400 / webhook error
Validating Admission Enforces policy — cannot modify 403
Schema Validation Enforces OpenAPI object structure 422
etcd persistence Writes object, assigns resourceVersion 500 on failure
Watch Cache fan-out Notifies all watchers with no etcd round-trip —
API Priority & Fairness Protects critical traffic under load 429 Too Many Requests

The API Server is not just a REST gateway. It is the policy engine, the validator, the state store, and the event bus for the entire cluster — all in one binary.

Hitesh Sahu
Written by Hitesh Sahu, a passionate developer and blogger.

Tue Jul 07 2026

Share This on

← Previous

Optimizing AI Inference at Scale: The Full Stack

Next →

etcd Architecture Explained

kubernetes/2-1-API-Server
Let's work together
+49 176-2019-2523
hiteshkrsahu@gmail.com
WhatsApp
Skype
Munich 🥨, Germany 🇩🇪, EU
Playstore
Hitesh Sahu's apps on Google Play Store
Need Help?
Let's Connect
Navigation
  Home/About
  Skills
  Work/Projects
  Lab/Experiments
  Contribution
  Awards
  Art/Sketches
  Thoughts
  Contact
Links
  Sitemap
  Legal Notice
  Privacy Policy

Made with

NextJS logo

NextJS by

hitesh Sahu

| © 2026 All rights reserved.