Home DevOps Kubernetes Admission Controllers
Advanced 5 min · July 12, 2026

Kubernetes Admission Controllers

Learn Kubernetes Admission Controllers with plain-English explanations and real examples..

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Verified
production tested
July 12, 2026
last updated
2,073
articles · all by Naren
Before you start⏱ 30 min
  • Kubernetes cluster (v1.25+), kubectl, Go (1.21+), basic understanding of Kubernetes API objects (Pod, Deployment, Service), TLS certificates, and HTTP servers.
✦ Definition~90s read
What is Kubernetes Admission Controllers?

Kubernetes Admission Controllers are plugins that intercept requests to the API server after authentication and authorization but before object persistence, enabling policy enforcement, mutation, and validation of resources. They matter because they are the last line of defense against misconfigurations and security violations in a cluster.

Think of Kubernetes Admission Controllers like a tool in your DevOps toolkit — once you understand what it does and when to reach for it, managing Kubernetes clusters becomes second nature.

Use them when you need to enforce organization-wide policies like resource limits, security contexts, or custom validations that cannot be expressed via RBAC or network policies alone.

Plain-English First

Think of Kubernetes Admission Controllers like a tool in your DevOps toolkit — once you understand what it does and when to reach for it, managing Kubernetes clusters becomes second nature.

A single misconfigured Pod can bring down a production cluster. In 2023, a major outage at a financial services firm was traced back to a developer deploying a container with privileged: true and hostNetwork: true, bypassing all existing security controls. The fix? Admission controllers. These plugins sit between the API server and etcd, intercepting every create, update, or delete request. They are the gatekeepers that can mutate, validate, or reject resources before they become reality. Unlike RBAC or network policies, admission controllers operate at the object level, giving you surgical control over what enters your cluster. If you're not using them, you're running a cluster with no safety net.

What Are Admission Controllers?

Admission controllers are compiled-in or webhook-based plugins that intercept Kubernetes API requests after authentication and authorization, but before the object is persisted in etcd. They can modify (mutating) or reject (validating) the request. Kubernetes ships with a set of built-in admission controllers (e.g., NamespaceLifecycle, LimitRanger, PodSecurityPolicy), but the real power comes from dynamic admission control via MutatingAdmissionWebhook and ValidatingAdmissionWebhook. These allow you to run custom logic in external HTTP servers. Every request to the API server—kubectl apply, a Deployment controller reconciling, a ServiceAccount token mount—passes through the configured chain of admission controllers. Understanding this pipeline is critical for debugging why a resource was rejected or modified unexpectedly.

check-admission-controllers.shBASH
1
kubectl api-resources --verbs=list -o name | xargs -I {} sh -c 'kubectl get --raw /api/v1/namespaces/default/{} 2>/dev/null | head -c 100'
Output
Error from server (Forbidden): admission webhook "deny-privileged.example.com" denied the request: Privileged containers are not allowed
🔥Admission vs Authorization
Admission controllers are not a replacement for RBAC. RBAC decides who can perform an action; admission controllers decide what can be created. They work together: RBAC gates the user, admission gates the object.
📊 Production Insight
In production, always enable the built-in 'NamespaceLifecycle' and 'LimitRanger' controllers to prevent resource starvation and ensure proper namespace isolation.
🎯 Key Takeaway
Admission controllers are the last checkpoint before an object is persisted—use them to enforce policies that cannot be expressed in RBAC or network policies.

Mutating vs Validating: The Two Flavors

Mutating admission controllers modify the incoming object before it is stored. They run first, in a chain, and can inject sidecars, set default resource limits, add labels, or patch security contexts. Validating admission controllers run after all mutating controllers, and can reject the request based on the final state of the object. The separation is crucial: mutating controllers can fix common omissions (e.g., adding a default PodSecurityContext), while validating controllers enforce hard rules (e.g., no privileged containers). Both are implemented as webhooks, but mutating webhooks must be idempotent—they may be called multiple times for the same request if the object is modified by another mutating webhook. A common pitfall is writing a mutating webhook that is not idempotent, leading to infinite loops or corrupted objects.

mutating-webhook.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: sidecar-injector.example.com
webhooks:
- name: sidecar-injector.example.com
  clientConfig:
    service:
      name: sidecar-injector
      namespace: default
      path: /mutate
    caBundle: <base64-encoded-ca>
  rules:
  - operations: ["CREATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
  admissionReviewVersions: ["v1"]
  sideEffects: None
  timeoutSeconds: 5
  reinvocationPolicy: IfNeeded
Output
MutatingWebhookConfiguration created
⚠ Idempotency is Mandatory
Mutating webhooks may be called multiple times for the same request. Ensure your mutation logic checks if the modification has already been applied (e.g., check for an annotation) to avoid duplicate sidecars or infinite loops.
📊 Production Insight
Set 'reinvocationPolicy: IfNeeded' on mutating webhooks to allow re-invocation after other mutating webhooks have run, ensuring your defaults are applied last.
🎯 Key Takeaway
Mutating webhooks modify objects before validation; validating webhooks reject non-compliant objects. Always make mutating webhooks idempotent.
kubernetes-admission-controllers THECODEFORGE.IO Admission Controller Request Flow Step-by-step processing of API requests through admission controllers API Request Received Authentication and authorization complete Mutating Admission Controllers Modify or inject resources (e.g., sidecar) Object Schema Validation Validate against OpenAPI schema Validating Admission Controllers Enforce policies (e.g., deny privileged) Persist to etcd Store final object in database ⚠ Order matters: mutating runs before validating Ensure webhooks are idempotent to avoid conflicts THECODEFORGE.IO
thecodeforge.io
Kubernetes Admission Controllers

Building a Validating Webhook: Deny Privileged Containers

Let's build a simple validating webhook that rejects any Pod with 'securityContext.privileged: true'. The webhook is an HTTP server that receives AdmissionReview requests and returns AdmissionReview responses. We'll use Go with the 'k8s.io/api/admission/v1' package. The server listens on /validate, decodes the request, checks the Pod spec, and returns 'allowed: false' with a message if privileged is set. Deploy it as a Service and register it via ValidatingWebhookConfiguration. This is a common pattern for enforcing security policies without modifying the cluster's built-in controllers.

main.goGO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	admissionv1 "k8s.io/api/admission/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func validate(w http.ResponseWriter, r *http.Request) {
	var review admissionv1.AdmissionReview
	if err := json.NewDecoder(r.Body).Decode(&review); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var pod corev1.Pod
	if err := json.Unmarshal(review.Request.Object.Raw, &pod); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	allowed := true
	message := ""
	if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.Privileged != nil && *pod.Spec.SecurityContext.Privileged {
		allowed = false
		message = "Privileged containers are not allowed"
	}

	review.Response = &admissionv1.AdmissionResponse{
		UID:     review.Request.UID,
		Allowed: allowed,
		Result:  &metav1.Status{Message: message},
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(review)
}

func main() {
	http.HandleFunc("/validate", validate)
	log.Fatal(http.ListenAndServeTLS(":443", "/etc/certs/tls.crt", "/etc/certs/tls.key", nil))
}
Output
Server started on :443
💡TLS is Mandatory
Kubernetes requires all webhook servers to use TLS. Use cert-manager to automate certificate provisioning, or generate self-signed certs and include the CA bundle in the webhook configuration.
📊 Production Insight
In production, always set 'timeoutSeconds' to a low value (e.g., 5) and implement a circuit breaker to avoid the webhook becoming a single point of failure.
🎯 Key Takeaway
A validating webhook is an HTTP server that returns 'allowed: false' to reject non-compliant resources. Always validate the request object thoroughly.

Building a Mutating Webhook: Inject a Sidecar Proxy

Mutating webhooks can automatically inject sidecar containers, such as a logging agent or service mesh proxy. The webhook patches the Pod spec to add a container, volume mounts, and annotations. The key is to use JSON Patch (RFC 6902) operations. For example, to inject an Envoy sidecar, you add a container to 'spec.containers' and a volume mount for the Envoy config. The webhook must be idempotent: check for an annotation like 'sidecar-injected: true' to avoid double injection. This pattern is used by Istio, Linkerd, and many internal platforms.

mutate.goGO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
func mutate(w http.ResponseWriter, r *http.Request) {
	var review admissionv1.AdmissionReview
	json.NewDecoder(r.Body).Decode(&review)

	var pod corev1.Pod
	json.Unmarshal(review.Request.Object.Raw, &pod)

	// Check if already injected
	if pod.Annotations["sidecar-injected"] == "true" {
		// Already injected, skip
		review.Response = &admissionv1.AdmissionResponse{
			UID:     review.Request.UID,
			Allowed: true,
			Patch:   nil,
			PatchType: func() *admissionv1.PatchType { pt := admissionv1.PatchTypeJSONPatch; return &pt }(),
		}
		json.NewEncoder(w).Encode(review)
		return
	}

	// Build JSON Patch to add sidecar container
	patch := []map[string]interface{}{
		{
			"op":    "add",
			"path":  "/spec/containers/-",
			"value": map[string]interface{}{
				"name":  "envoy",
				"image": "envoyproxy/envoy:v1.28-latest",
				"ports": []map[string]interface{}{{"containerPort": 9901}},
			},
		},
		{
			"op":    "add",
			"path":  "/metadata/annotations/sidecar-injected",
			"value": "true",
		},
	}

	patchBytes, _ := json.Marshal(patch)

	review.Response = &admissionv1.AdmissionResponse{
		UID:     review.Request.UID,
		Allowed: true,
		Patch:   patchBytes,
		PatchType: func() *admissionv1.PatchType { pt := admissionv1.PatchTypeJSONPatch; return &pt }(),
	}

	json.NewEncoder(w).Encode(review)
}
Output
Pod mutated with sidecar container
⚠ Patch Order Matters
JSON Patch operations are applied sequentially. If you add a container and then modify an existing container, the indices may shift. Use 'add' with '-' to append, or use strategic merge patch if available.
📊 Production Insight
In production, use a 'reinvocationPolicy: IfNeeded' and ensure your webhook can handle being called multiple times without side effects.
🎯 Key Takeaway
Mutating webhooks use JSON Patch to modify objects. Always check for idempotency annotations to avoid double mutations.

Webhook Configuration: Rules, Failure Policy, and Timeouts

A webhook configuration defines which resources trigger the webhook via 'rules' (operations, apiGroups, apiVersions, resources, scope). You can also set 'failurePolicy' to 'Fail' (default) or 'Ignore'. 'Fail' means if the webhook is unreachable, the request is rejected—use for security-critical webhooks. 'Ignore' allows the request to proceed if the webhook is down—use for non-critical mutations like sidecar injection. 'timeoutSeconds' should be set low (e.g., 5-10) to avoid blocking the API server. 'sideEffects' must be 'None' or 'NoneOnDryRun' to indicate the webhook has no side effects beyond admission. 'reinvocationPolicy' controls whether the webhook is called again after other mutating webhooks. Proper configuration prevents cluster-wide outages.

validating-webhook-config.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: deny-privileged.example.com
webhooks:
- name: deny-privileged.example.com
  clientConfig:
    service:
      name: deny-privileged
      namespace: default
      path: /validate
    caBundle: <base64-ca>
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
    scope: "Namespaced"
  failurePolicy: Fail
  matchPolicy: Equivalent
  timeoutSeconds: 5
  sideEffects: None
  admissionReviewVersions: ["v1"]
Output
ValidatingWebhookConfiguration created
🔥matchPolicy: Equivalent vs Exact
Use 'Equivalent' to match requests for resources that are equivalent across API versions (e.g., apps/v1 and extensions/v1beta1). 'Exact' only matches the exact version specified.
📊 Production Insight
Always set 'timeoutSeconds' to 5 or less. Long timeouts can cause API server congestion and cascading failures.
🎯 Key Takeaway
Configure failurePolicy, timeoutSeconds, and sideEffects carefully to balance security and availability. Use 'Fail' for critical validations, 'Ignore' for best-effort mutations.

Debugging Admission Webhooks: Common Pitfalls

Admission webhooks can be tricky to debug. Common issues include: TLS certificate mismatches (CA bundle not matching server cert), webhook service unreachable (wrong namespace or port), infinite loops (mutating webhook modifies an object that triggers itself), and timeouts. Use 'kubectl describe validatingwebhookconfiguration' to see the configuration. Check the API server logs (often in /var/log/kube-apiserver.log) for admission errors. Enable verbose logging on your webhook server. A common mistake is forgetting to set 'sideEffects: None'—without it, the API server will not call the webhook on dry-run requests. Also, ensure your webhook handles both CREATE and UPDATE operations if needed.

debug-webhook.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
# Check webhook configuration
kubectl get validatingwebhookconfiguration -o yaml

# Check API server logs for admission errors
kubectl logs -n kube-system kube-apiserver-controlplane | grep -i admission

# Test webhook with dry-run
kubectl apply -f pod.yaml --dry-run=server

# Simulate an admission request with curl
curl -k -X POST https://webhook-service:443/validate \
  -H "Content-Type: application/json" \
  -d @admission-review.json
Output
{"response":{"uid":"...","allowed":false,"status":{"metadata":{},"message":"Privileged containers are not allowed","code":403}}}
⚠ Don't Forget Dry-Run
If your webhook has side effects (e.g., writing to a database), set 'sideEffects: NoneOnDryRun' and skip the side effect when the request is a dry-run. Otherwise, dry-run requests will fail.
📊 Production Insight
In production, implement health checks and metrics on your webhook server. Use Prometheus to monitor request latency and error rates, and set up alerts for timeouts.
🎯 Key Takeaway
Debug admission webhooks by checking API server logs, using dry-run, and verifying TLS configuration. Always test with a dry-run first.

Built-in Admission Controllers You Should Know

Kubernetes ships with several built-in admission controllers that are enabled by default in most distributions. 'NamespaceLifecycle' prevents creation of resources in terminating namespaces. 'LimitRanger' enforces default and maximum resource limits per namespace. 'ResourceQuota' enforces namespace-level resource quotas. 'PodSecurity' (replaces PodSecurityPolicy) enforces Pod Security Standards (privileged, baseline, restricted). 'NodeRestriction' limits kubelet's self-modification capabilities. 'AlwaysPullImages' ensures images are always pulled, preventing use of stale or malicious images. Understanding these built-in controllers helps you avoid reinventing the wheel. For example, instead of writing a custom webhook to enforce resource limits, use LimitRanger and ResourceQuota.

list-admission-controllers.shBASH
1
2
3
4
5
# List enabled admission controllers (kube-apiserver flags)
kubectl get pods -n kube-system -l component=kube-apiserver -o jsonpath="{.items[0].spec.containers[0].command}" | tr ' ' '\n' | grep enable-admission-plugins

# Example output:
# --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,PodSecurity,DefaultStorageClass,DefaultTolerationSeconds,ResourceQuota
Output
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,PodSecurity,DefaultStorageClass,DefaultTolerationSeconds,ResourceQuota
💡Don't Disable Built-in Controllers
Built-in controllers like NamespaceLifecycle and NodeRestriction are essential for cluster stability. Only disable them if you have a very specific reason and understand the consequences.
📊 Production Insight
In production, always enable 'PodSecurity' with the 'restricted' profile as a baseline, and use 'baseline' for namespaces that require relaxed policies.
🎯 Key Takeaway
Leverage built-in admission controllers before writing custom webhooks. They cover common use cases like resource limits, pod security, and namespace lifecycle.
kubernetes-admission-controllers THECODEFORGE.IO Admission Webhook Architecture Layered components for mutating and validating webhooks User/Client kubectl | API client | Automation API Server Authentication | Authorization | Admission chain Webhook Configuration MutatingWebhookConfiguration | ValidatingWebhookConfiguration Webhook Service Mutating webhook | Validating webhook Backend Storage etcd THECODEFORGE.IO
thecodeforge.io
Kubernetes Admission Controllers

Admission Controllers and Pod Security Standards

Pod Security Standards (PSS) define three policies: privileged, baseline, and restricted. The 'PodSecurity' admission controller (GA since v1.25) enforces these policies at the namespace level via labels. You can set 'pod-security.kubernetes.io/enforce', 'pod-security.kubernetes.io/audit', and 'pod-security.kubernetes.io/warn' labels on namespaces. This replaces the deprecated PodSecurityPolicy (PSP). PSS is simpler and more maintainable. However, for fine-grained control, you may still need custom webhooks. For example, PSS does not allow you to block specific image registries or enforce specific seccomp profiles. In those cases, combine PSS with a validating webhook.

namespace-pss.yamlYAML
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted
Output
Namespace created with Pod Security Standards
🔥PSS vs Custom Webhooks
Use PSS for standard pod security. Use custom webhooks for policies that PSS cannot express, such as 'containers must not use the latest tag' or 'must have resource limits'.
📊 Production Insight
In production, start with 'baseline' policy and gradually move to 'restricted' as teams adapt. Use 'audit' and 'warn' modes to detect violations without breaking existing workloads.
🎯 Key Takeaway
Pod Security Standards provide a built-in way to enforce pod security. Use them as a baseline and supplement with custom webhooks for advanced policies.

Performance Considerations and Best Practices

Admission webhooks add latency to every API request. A slow webhook can cripple the API server. Best practices: keep webhooks stateless and fast (sub-millisecond response times). Use caching for expensive operations (e.g., fetching data from external APIs). Set 'timeoutSeconds' low (5s) and implement a circuit breaker to fail fast. Use 'failurePolicy: Ignore' for non-critical webhooks. Deploy multiple replicas behind a Service for high availability. Monitor webhook latency and error rates with Prometheus. Avoid making network calls to external services inside the webhook—they can fail and block the cluster. If you must, use a local cache or a sidecar that pre-fetches data.

webhook-deployment.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
apiVersion: apps/v1
kind: Deployment
metadata:
  name: admission-webhook
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: admission-webhook
  template:
    metadata:
      labels:
        app: admission-webhook
    spec:
      containers:
      - name: webhook
        image: myregistry/admission-webhook:v1.0
        ports:
        - containerPort: 443
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 500m
            memory: 256Mi
        livenessProbe:
          httpGet:
            path: /healthz
            port: 443
            scheme: HTTPS
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /readyz
            port: 443
            scheme: HTTPS
          initialDelaySeconds: 5
          periodSeconds: 10
Output
Deployment created with 3 replicas
⚠ Avoid External Dependencies
A webhook that calls an external API (e.g., to check an image registry) can become a bottleneck or single point of failure. Cache data locally or use a sidecar to pre-fetch.
📊 Production Insight
In production, set up a dedicated ServiceAccount for the webhook with minimal RBAC permissions, and use NetworkPolicy to restrict egress traffic.
🎯 Key Takeaway
Admission webhooks must be fast and reliable. Use caching, low timeouts, and multiple replicas to minimize impact on API server performance.

Advanced Patterns: Chaining and Dynamic Policies

Multiple admission webhooks can be chained. The order of execution is determined by the webhook name (alphabetical) for mutating webhooks, and then validating webhooks run in parallel. You can use 'reinvocationPolicy' to re-run mutating webhooks after others have run. This allows patterns like: first webhook adds default labels, second webhook injects sidecar, third webhook validates final state. Dynamic policies can be implemented by having the webhook fetch policy from a ConfigMap or an external policy engine (e.g., OPA/Gatekeeper). Gatekeeper is a popular project that uses admission webhooks with a declarative policy language (Rego). It provides a framework for creating, managing, and auditing policies.

deny-privileged.regoREGO
1
2
3
4
5
6
7
8
9
10
package k8s.admission

import data.k8s.pods

violation[{"msg": msg}] {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]
    container.securityContext.privileged == true
    msg := sprintf("Container %v is privileged", [container.name])
}
Output
ConstraintTemplate and Constraint created
💡Use Gatekeeper for Complex Policies
Instead of writing custom webhooks for every policy, use Gatekeeper (OPA) to manage policies declaratively. It handles webhook lifecycle, auditing, and dry-run.
📊 Production Insight
In production, use Gatekeeper's audit feature to detect violations without blocking, and gradually enforce policies after fixing existing violations.
🎯 Key Takeaway
Chain mutating webhooks in a specific order and use Gatekeeper for complex, dynamic policies. ReinvocationPolicy allows fine-grained control over mutation order.

Testing Admission Webhooks Locally

Before deploying to a cluster, test your webhook locally. Use 'kubectl create --dry-run=server' to simulate an admission request. You can also use the 'admission-webhook-bootstrapper' tool or write unit tests that send AdmissionReview JSON to your server. For integration testing, set up a local kind cluster and deploy the webhook. Use 'curl' to send test requests directly to the webhook endpoint. Ensure your webhook handles edge cases: empty objects, missing fields, and multiple containers. Test both allowed and denied scenarios. Automate these tests in CI/CD to prevent regressions.

test-webhook.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Create a test AdmissionReview request
cat > test-request.json <<EOF
{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "request": {
    "uid": "test-uid",
    "operation": "CREATE",
    "object": {
      "apiVersion": "v1",
      "kind": "Pod",
      "metadata": {
        "name": "test-pod"
      },
      "spec": {
        "containers": [{
          "name": "test",
          "image": "nginx",
          "securityContext": {
            "privileged": true
          }
        }]
      }
    }
  }
}
EOF

# Send to webhook
curl -k -X POST https://localhost:8443/validate \
  -H "Content-Type: application/json" \
  -d @test-request.json
Output
{"response":{"uid":"test-uid","allowed":false,"status":{"metadata":{},"message":"Privileged containers are not allowed","code":403}}}
💡Use Dry-Run for Safe Testing
Always test with '--dry-run=server' before enforcing a new webhook. This shows you what would be rejected without actually blocking anything.
📊 Production Insight
In production, run a canary deployment of new webhooks with 'failurePolicy: Ignore' and monitor for errors before switching to 'Fail'.
🎯 Key Takeaway
Test webhooks locally with curl and AdmissionReview JSON. Use dry-run in the cluster to validate behavior before enforcing.
Mutating vs Validating Admission Controllers Key differences in behavior and use cases Mutating Validating Primary function Modify or inject resources Reject or allow requests Execution order Runs first in admission chain Runs after mutating controllers Common use case Inject sidecar proxies, set defaults Enforce security policies, deny privileg Idempotency required Yes, to avoid duplicate modifications Not required, but recommended Response type Returns modified object or patch Returns allowed or denied decision THECODEFORGE.IO
thecodeforge.io
Kubernetes Admission Controllers

Production Deployment: Certificates, Monitoring, and Rollback

Deploying admission webhooks in production requires careful planning. Use cert-manager to automate TLS certificate provisioning and rotation. Monitor webhook latency, error rates, and request volume with Prometheus. Set up alerts for high latency or error spikes. Implement a circuit breaker: if the webhook fails consecutively, stop calling it and fall back to 'failurePolicy: Ignore' temporarily. Have a rollback plan: if a webhook causes issues, delete the webhook configuration to disable it. Use feature flags or toggles to enable/disable webhooks without redeploying. Document the webhook's purpose, failure mode, and rollback procedure.

cert-manager-issuer.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: selfsigned-issuer
  namespace: default
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: admission-webhook-cert
  namespace: default
spec:
  secretName: admission-webhook-tls
  dnsNames:
  - admission-webhook.default.svc
  issuerRef:
    name: selfsigned-issuer
    kind: Issuer
Output
Issuer and Certificate created
⚠ Certificate Rotation
Certificates expire. Use cert-manager to automate renewal. Update the CA bundle in the webhook configuration when the CA certificate changes.
📊 Production Insight
In production, use a dedicated namespace for admission webhooks with NetworkPolicy to restrict access, and run multiple replicas across availability zones.
🎯 Key Takeaway
Productionize webhooks with automated TLS, monitoring, circuit breakers, and rollback plans. Always have a way to disable a webhook quickly.
⚙ Quick Reference
12 commands from this guide
FileCommand / CodePurpose
check-admission-controllers.shkubectl api-resources --verbs=list -o name | xargs -I {} sh -c 'kubectl get --ra...What Are Admission Controllers?
mutating-webhook.yamlapiVersion: admissionregistration.k8s.io/v1Mutating vs Validating
main.go"encoding/json"Building a Validating Webhook
mutate.gofunc mutate(w http.ResponseWriter, r *http.Request) {Building a Mutating Webhook
validating-webhook-config.yamlapiVersion: admissionregistration.k8s.io/v1Webhook Configuration
debug-webhook.shkubectl get validatingwebhookconfiguration -o yamlDebugging Admission Webhooks
list-admission-controllers.shkubectl get pods -n kube-system -l component=kube-apiserver -o jsonpath="{.items...Built-in Admission Controllers You Should Know
namespace-pss.yamlapiVersion: v1Admission Controllers and Pod Security Standards
webhook-deployment.yamlapiVersion: apps/v1Performance Considerations and Best Practices
deny-privileged.regoviolation[{"msg": msg}] {Advanced Patterns
test-webhook.shcat > test-request.json <Testing Admission Webhooks Locally
cert-manager-issuer.yamlapiVersion: cert-manager.io/v1Production Deployment

Key takeaways

1
Admission Controllers are the Last Line of Defense
They intercept API requests after authn/authz and before persistence, enabling policy enforcement that cannot be achieved with RBAC or network policies alone.
2
Mutating vs Validating
Mutating webhooks modify objects (e.g., inject sidecars, set defaults) and must be idempotent. Validating webhooks reject non-compliant objects. Both are essential for a secure cluster.
3
Performance Matters
Admission webhooks add latency to every API request. Keep them fast, stateless, and use caching. Set low timeouts and use 'failurePolicy: Ignore' for non-critical webhooks.
4
Use Built-in Controllers First
Kubernetes ships with powerful built-in admission controllers like LimitRanger, ResourceQuota, and PodSecurity. Leverage them before writing custom webhooks.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the difference between an admission controller and a webhook?
Q02JUNIOR
Can admission controllers modify existing resources?
Q03JUNIOR
How do I debug a webhook that is silently failing?
Q01 of 03JUNIOR

What is the difference between an admission controller and a webhook?

ANSWER
An admission controller is a general concept: a plugin that intercepts API requests. A webhook is a specific type of admission controller that calls an external HTTP server. Built-in admission control
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
What is the difference between an admission controller and a webhook?
02
Can admission controllers modify existing resources?
03
How do I debug a webhook that is silently failing?
04
What happens if my webhook is down?
05
Can I use admission controllers to enforce policies on custom resources?
06
How do I ensure idempotency in a mutating webhook?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Verified
production tested
July 12, 2026
last updated
2,073
articles · all by Naren
🔥

That's Kubernetes. Mark it forged?

5 min read · try the examples if you haven't

Previous
Kubernetes Cluster Autoscaler — Deep Dive
28 / 38 · Kubernetes
Next
Kubernetes CRDs and Operators