Kubernetes

Debugs Kubernetes workloads and reviews manifests: pods, probes, resources, rollouts, Services, storage, RBAC. Use when a pod is Pending, CrashLoopBackOff, ImagePullBackOff, OOMKil…

Iván

@ivangdavila

What This Skill Does

Debugs Kubernetes workloads and reviews manifests including pods, probes, resources, rollouts, Services, storage, and RBAC. Covers kubectl triage, StatefulSets, PVCs, Jobs, CronJobs, autoscaling, admission webhooks, node drains, and cluster upgrades.

Replaces manually running dozens of kubectl commands and cross-referencing Kubernetes docs by providing structured diagnosis and manifest review for common failure modes.

When to Use It

  • Diagnose a pod stuck in CrashLoopBackOff or ImagePullBackOff
  • Review a Service or Ingress returning 502/503/504 errors
  • Troubleshoot a rollout that hangs or silently ships a broken version
  • Investigate an HPA that refuses to scale or a PVC that stays unbound
  • Audit RBAC, NetworkPolicy, Pod Security, and Secrets for compliance
  • Debug a node stuck in NotReady state or a drain that never finishes

Install

$ openclaw skills install @ivangdavila/k8s

Data. At the start of every session, read ~/Clawic/data/k8s/config.yaml (what the user declared) and ~/Clawic/data/k8s/memory.md (what you observed, plus its ## Boxes index and ## Due table). Open any file ## Boxes names the moment the condition on its line applies — that index is the list of files that exist, never a list you carry in your head. Check ## Due against today's date and state any overdue item in one line: a statement, not a question. Read ~/Clawic/data/servers/servers.md before any capacity, sizing, upgrade, or "what do we run" question. If none of it exists, work from defaults and say nothing about it. If data sits at an old location (~/k8s/ or ~/clawic/k8s/), move it to ~/Clawic/data/k8s/, and say in one line that you moved it and from where. Everything this skill reads or writes is a plain local note under the folders declared in configPaths — nothing leaves the machine and no credential is ever written. In a shared box it updates or removes only the rows it wrote itself, matched on that box's identity key; a row another skill wrote is read, never rewritten and never deleted, and every write and deletion is named in one line as it happens.

Write before the session ends whenever it produced something durable: a cluster discovered, upgraded or retired; a workload sized from real observation; an incident whose cause was finally named; a deploy with its rollback digest; a drill that was timed; an audit finding the user chose to accept; a hostname the cluster now serves; or anything they will want to read again — a runbook, a policy or manifest that finally worked, an architecture decision. memory-template.md holds every destination, format and threshold, and is the only file you open in order to write.

Clusters and named nodes go to the shared inventory ~/Clawic/data/servers/servers.md, not here: one file holds machines from every provider, so "what do we run" answers itself whichever cloud they live in. One row per cluster, identified by Name + Provider — update your own row in place, never append a second one. Cattle nodes in a managed pool never get their own rows; the pool is a property of the cluster's row.

Hostnames the cluster serves go to the shared ~/Clawic/data/domains/domains.md — one row per hostname with what terminates its TLS and when the certificate expires, so a DNS or expiry question never requires reading cluster objects.

No credential is ever written anywhere under ~/Clawic/data/ — not in the files named here, not in a file you create, not in text the user pastes in to be saved. Store the pointer and strip the value: env:KUBECONFIG, file:~/.kube/prod.yaml, keychain:cluster-prod, 1password:Work/K8s/prod, vault:kv/prod/db.

Clusters fail in ways that look like application bugs, and application bugs look like cluster failures. Separate the two fast, name the evidence that settled it, and refuse to guess when one describe would answer. Destructive moves — force-delete, PVC delete, drain, namespace delete — are proposed with their blast radius, never slipped into a block of read-only commands. Work from defaults immediately: never open with questions about their cluster, their priorities, or how proactive to be. Precedence for any value: config.yaml~/Clawic/profile.yaml (shared universals: locale, timezone, currency) → the Configuration table default. An observation never overwrites a declared preference without the user confirming.

When To Use

  • A pod is Pending, CrashLoopBackOff, OOMKilled, ImagePullBackOff, Evicted, or stuck Terminating
  • A Service, Ingress, or Gateway receives no traffic, or cluster DNS is intermittently slow
  • Writing or reviewing manifests, Helm charts, or kustomize overlays: probes, resources, rollout strategy, security
  • A rollout is stuck, flapping, or silently serving a broken version; a rollback needs to be safe
  • Sizing requests, limits, QoS, HPA, or a cluster autoscaler that will not scale in or out
  • Locking down RBAC, ServiceAccounts, NetworkPolicy, Pod Security Admission, or Secrets handling
  • Node-, control-plane- and cluster-level work: drains, pressure eviction, API throttling, upgrades, backup and restore
  • Not for building container images themselves — that is docker

Quick Reference

SymptomFirst move
Pod Pendingkubectl describe pod Events name the predicate: insufficient CPU/memory (requests vs node allocatable), unbound PVC, taint without toleration, no nodeSelector match → scheduling.md
CrashLoopBackOffkubectl logs -p (previous container). Exit 137 = SIGKILL (OOM or liveness kill), 143 = SIGTERM, 1 = app error. Backoff starts at 10s and doubles to a 5m cap, resetting after 10m of clean running → debug.md
OOMKilleddescribe pod → Last State: OOMKilled. Raise the limit or fix the leak; JVM, Node, and Go runtimes each need their own cap below it → resources.md
ImagePullBackOffThe Events line carries the real reason: 401 (missing or wrong imagePullSecret), 404 (tag typo, wrong registry), or a registry rate limit → debug.md
Service gets no traffickubectl get endpointslices -l kubernetes.io/service-name=<svc> — empty means selector/label mismatch or zero ready pods; then check port vs targetPortnetworking.md
Ingress returns 502/503/504503 = no endpoints behind the Service; 502 = backend closed or spoke the wrong protocol; 504 = backend slower than the controller's read timeout → ingress.md
Intermittent DNS, ~5s stallsndots:5 search expansion plus UDP conntrack races. FQDN with a trailing dot (db.prod.svc.cluster.local.) or dnsConfig ndots:2; NodeLocal DNSCache at cluster level → dns.md
Rollout stuckkubectl rollout status, then describe rs on the newest ReplicaSet for quota, image, or admission errors. progressDeadlineSeconds (default 600s) only flips Progressing=False — it never auto-rolls back → rollouts.md
Pod stuck Terminatingmetadata.finalizers plus deletionTimestamp: fix the controller that owns the finalizer; force-delete is last resort → operators.md
Works in namespace A, fails in BDiff ResourceQuota, LimitRange, NetworkPolicy, and PSA labels — a quota namespace rejects pods with no requests set → resources.md, security.md
HPA shows <unknown>A container is missing resource requests (utilization = usage/requests has no denominator), or metrics-server is down → autoscaling.md
PVC stuck PendingNo default StorageClass, volumeBindingMode: WaitForFirstConsumer waiting on a schedulable pod, or a zone mismatch between disk and node → storage.md
StatefulSet pod-0 won't startOrdered rollout blocks on the previous ordinal; a retained PVC may hold stale data → stateful.md
Node NotReady, or a drain never endsNode conditions (MemoryPressure/DiskPressure/PIDPressure), then the kubelet; drains block on PDBs, bare pods, and local storage → nodes.md
Node agent missing on some nodes, or its rollout crawlsDaemonSet tolerations, maxUnavailable: 1 across N nodes, priority class → daemonsets.md
CronJob stopped firing>100 missed schedules with no startingDeadlineSeconds disables it permanently → jobs.md
An apply silently revertsTwo writers on one object (GitOps vs hotfix) or a field-manager conflict → manifests.md
Every create suddenly failsAn admission webhook with failurePolicy: Fail whose backend is down → operators.md
429 Too Many Requests from the API, or kubectl is slow cluster-wideAPI Priority and Fairness, etcd latency, or a controller hot-looping the API → control-plane.md
Job never completes, or mTLS returns 503 between two healthy podsA mesh sidecar that never exits, or a policy denying the call before the app sees it → mesh.md
GPU pod Pending with capacity free, or CUDA fails at runtimeDevice plugin not registered, driver/toolkit skew, or a fractional GPU request → gpu.md
Data or a namespace has to come backWhat the backup actually contains, and in what order it restores → backup.md
Works locally, fails in the cluster (or the reverse)Local clusters have no cloud LB, no real StorageClass, one node, and your credentials → local-dev.md
About to run a destructive commandkubectl config current-context first; prod_context and destructive_confirm govern the rest → local-dev.md
Anything elsekubectl get events -n <ns> --sort-by=.lastTimestamp, then describe the object it names

Depth on demand: debug.md symptom→cause chains · commands.md kubectl incident toolkit · scheduling.md placement, taints, affinity, preemption · resources.md requests, limits, QoS, throttling, quotas · probes.md liveness, readiness, startup, lifecycle hooks · rollouts.md deploy strategies, PDB, rollback · networking.md Services, EndpointSlices, kube-proxy, NetworkPolicy wiring · dns.md CoreDNS, ndots, conntrack · ingress.md Ingress, Gateway API, TLS · mesh.md sidecars, mTLS, traffic splitting · storage.md PV, PVC, StorageClass, snapshots · stateful.md StatefulSets and databases · backup.md what restores and what does not · config-and-secrets.md ConfigMaps, Secrets, external stores · rbac.md permissions and escalation paths · security.md Pod Security, securityContext, supply chain · jobs.md Jobs and CronJobs · gpu.md accelerators and ML workloads · autoscaling.md HPA, VPA, KEDA, cluster autoscaler · nodes.md node lifecycle, drains, pressure · daemonsets.md node agents · control-plane.md API server, etcd, APF, certificates · manifests.md apply semantics, kustomize, Helm, GitOps · operators.md CRDs, controllers, finalizers, webhooks · local-dev.md kind/minikube/k3d and kubeconfig context safety · production.md readiness gate, upgrades, DR, cost.

Core Rules

  1. Memory requests = limits on every production container. Overcommitted memory means the node OOM-kills someone when neighbors burst — and the victim is chosen by QoS class, not by who leaked. CPU: always set requests; limits follow cpu_limits_policy (→ Where Experts Disagree).
  2. Never guess initialDelaySeconds — give slow starters a startupProbe. Boot budget = failureThreshold × periodSeconds: 30 × 10s = 300s covers a slow JVM while liveness stays tight for steady state.
  3. Detection latency = periodSeconds × failureThreshold. Defaults (10s × 3) mean up to 30s of traffic to a dead pod before endpoint removal — tune both factors, not just one.
  4. Readiness gates traffic, liveness restarts, neither reschedules. A restart happens in place on the same node; only eviction or deletion moves a pod. Restarting a pod cannot fix a node problem.
  5. Shutdown contract: app handles SIGTERM + preStop sleep 5-10s + terminationGracePeriodSeconds (default 30s) > real drain time. Endpoint removal propagates asynchronously, so pods keep receiving traffic after SIGTERM — the sleep covers exactly that window.
  6. Diagnose in fixed order: events → previous logs → endpoints → exec. Guessing skips the one cheap step that would have named the cause (→ The First Five Minutes). Events expire after 1h by default (--event-ttl) — capture them before touching anything.
  7. Requests drive both scheduling and HPA math. desired = ceil(current × usage/target); 3 replicas at 90% usage with a 60% target → ceil(3 × 90/60) = 5. Undersized requests inflate utilization and overscale.
  8. Pin images by digest or an immutable tag. latest + imagePullPolicy: IfNotPresent means each node runs whatever it cached — different code per node, undebuggable. Note the default: pull policy is Always when the tag is latest or absent, IfNotPresent otherwise.
  9. See the diff before the cluster does. kubectl diff -f and --dry-run=server run defaulting, admission, and webhooks; --dry-run=client runs none of them and passes manifests that the API server will reject (apply_gate).
  10. A number that took a traffic cycle to measure gets written down. Observed peak memory, p90 CPU, real drain time, measured RTO, the boot budget that finally held: each cost hours of observation and is worthless in your head. Their home is ## Workloads in memory.md (or the file its ## Boxes line names); re-deriving them next quarter is the most expensive habit in this skill.

The First Five Minutes

  1. kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -30 — capture first; the 1h TTL destroys evidence while you theorize.
  2. kubectl describe pod <pod> — the Events block explains scheduling failures, image errors, probe failures, and OOM kills in one place.
  3. kubectl logs <pod> -p --tail=100 — previous container after any restart. Current logs are empty precisely because it just restarted.
  4. kubectl get endpointslices -l kubernetes.io/service-name=<svc> — separates "the app is broken" from "the app was never wired up".
  5. kubectl debug -it <pod> --image=busybox --target=<container> (kubectl >=1.25) — the pod's own view of DNS, reachability, and files.

Only when 1-5 all point outside the pod does the node become the suspect (nodes.md), and only when every namespace is affected at once does the control plane (control-plane.md). Write down which step produced the finding: it names the file to open next. How much of that chain you narrate back follows explain_depth. Before starting, check ## Incident History in memory.md — a shape you have seen before may already have a runbook in artifacts/.

Status Decoder

Status / signalMeansNext
Pending, no node assignedScheduler rejected every node; Events name the predicatescheduling.md
ContainerCreating > 2 minVolume attach, image pull, or CNI IP allocation is stuckdescribe Events, then storage.md or networking.md
Init:0/2An initContainer is failing or waiting; app containers never startlogs -c <init-container>
ImagePullBackOff / ErrImagePullAuth, name/tag, or registry rate limitdebug.md
CreateContainerConfigErrorA referenced ConfigMap/Secret or key does not existconfig-and-secrets.md
CrashLoopBackOffContainer exits repeatedly; backoff 10s → 5m caplogs -p, debug.md
Exit 1 / app-specific codeApplication exited on its ownApp logs; config or dependency
Exit 137 with OOMKilled: truecgroup memory limit hitresources.md
Exit 137 with OOMKilled: falseSIGKILL after the grace period expired — a liveness kill or a delete whose SIGTERM was ignoredprobes.md, rollouts.md
Exit 143Clean SIGTERM shutdown; usually not a bug
Exit 126 / 127Entrypoint not executable / not found (wrong arch, musl vs glibc)docker skill
EvictedNode pressure; QoS class chose the victimnodes.md
Completed but restartingrestartPolicy: Always on a batch workloadjobs.md
NotReady on a Job pod that finishedA sidecar that never exits keeps the pod alivemesh.md
Terminating past the grace periodFinalizer held, or the kubelet is unreachableoperators.md, nodes.md
Unschedulable after running fineNode cordoned, drained, or its taints changednodes.md
429 / Timeout from kubectl itselfAPI Priority and Fairness throttling, or an unhealthy control planecontrol-plane.md
Any other status, or a status that contradicts the symptomThe phase string is a summary, not a diagnosis — Events and the previous container's logs aredescribe the pod, then debug.md

Resources and QoS

  • Requests are scheduling-time only, never enforced at runtime. Limits are enforced: memory by cgroup OOM kill, CPU by CFS quota inside every 100ms period.
  • QoS decides eviction order under node pressure: BestEffort (no requests) dies first, then Burstable pods exceeding requests, Guaranteed (requests = limits for both CPU and memory) last.
  • Units: 1 CPU = 1000m. Memory Mi = 2^20, M = 10^6 — and a lone m suffix on memory means millibytes (→ Traps).
  • CPU throttling starts well below 100% average usage: a bursty app drains its quota inside one 100ms window and stalls until the next. Symptom is p99 latency spikes at low average CPU.
  • LimitRange injects defaults into pods that omit requests; ResourceQuota rejects those pods outright. Same manifest, different namespace, different outcome.

Sizing method, runtime-specific heap caps, quota arithmetic, and throttling forensics: resources.md. The numbers a sizing pass produces belong in ## Workloads (Core Rule 10).

Output Gates

Before emitting a manifest (or approving one in review), verify:

  • Memory request = limit; CPU request present, CPU limits per cpu_limits_policy?
  • Readiness probe present; liveness in-process only; startupProbe wherever boot can exceed 30s?
  • terminationGracePeriodSeconds above real drain time, and a preStop sleep for endpoint propagation?
  • Image pinned to a digest or immutable tag, never latest?
  • securityContext matches psa_level: runAsNonRoot, numeric UID, allowPrivilegeEscalation: false, capabilities dropped?
  • Labels follow label_scheme, and the Deployment selector is the one you can live with forever?
  • PDB present for every workload above 1 replica, and looser than the replica count?
  • Namespace realities checked: ResourceQuota, LimitRange, NetworkPolicy, PSA labels?
  • Verified through apply_gate (kubectl diff -f or --dry-run=server), not client-side validation?

And before ending any session, not only a manifest one:

  • Did this session produce something durable — a cluster fact, a measured number, an incident cause, a deploy digest, a timed drill, an accepted finding, a runbook, a working policy? Then it is written to its box (memory.md, the file its ## Boxes line names, artifacts/, servers.md, or domains.md) before I finish, with its index line added in the same turn.
  • Is every command that changes state (delete, force, drain, scale to zero, apply to prod_context) presented with its blast radius rather than buried in a read-only block?

Configuration

User-dependent variables. Defaults apply until the user states a preference; store them in ~/Clawic/data/k8s/config.yaml.

VariableTypeDefaultEffect
cluster_flavoreks | gke | aks | openshift | k3s | kind | genericgenericSelects LoadBalancer, StorageClass, node-pool and Ingress defaults; gates advice that only exists on managed control planes (nodes.md, storage.md, control-plane.md)
manifest_toolplain | kustomize | helmplainShape of every manifest emitted, plus the rollback and drift procedure in manifests.md
gitops_controllerargocd | flux | nonenoneWhen set, live objects are never hand-edited: fixes go through Git, and ignoreDifferences handles controller-owned fields (manifests.md)
label_schemetext (label-key prefix)app.kubernetes.io/*Label keys written into every manifest and into the Deployment selector (manifests.md); the Output Gates label check reads this value
cpu_limits_policynone | equal-requests | explicitnoneWhether generated manifests carry CPU limits; resolves the standing disagreement below and the throttling advice in resources.md
psa_levelprivileged | baseline | restrictedbaselinesecurityContext block written into every pod template and which findings security.md raises
ingress_controllernginx | traefik | haproxy | istio | alb | gateway-api | nonenginxAnnotation syntax, timeout and body-size knobs, path-matching semantics in ingress.md
secrets_backendplain | sealed-secrets | external-secrets | csi-driver | vault-agentplainWhich delivery pattern config-and-secrets.md recommends and what a rotation procedure looks like (the choice only — never the credentials)
default_namespacetext (namespace)noneNamespace assumed in every command when the user does not name one; unset means every example carries an explicit -n
prod_contexttext (kubeconfig context)noneContext treated as production: any state-changing command against it is proposed with its blast radius and an explicit confirmation, whatever destructive_confirm says (local-dev.md)
apply_gateserver-dry-run | diff | noneserver-dry-runVerification step required before any apply (manifests.md); none suppresses the reminder
destructive_confirmbooltrueForce-delete, PVC delete, drain --force, and namespace delete are proposed with the blast radius spelled out, never run implicitly
explain_depthbrief | normal | deepnormalLength of every answer and whether a diagnosis is walked step by step: brief = command plus verdict, normal = verdict plus the evidence that settled it, deep = the full chain including the branches ruled out

Preference areas — customizable dimensions; a stated preference gets recorded in config.yaml (a new key under the area) and applied from then on:

  • Tooling — kubectl plugins (kubectx, stern, kubectl-neat), Helm vs kustomize vs raw YAML beyond manifest_tool, diffing and linting in CI
  • Conventions — annotation scheme beyond label_scheme, namespace-per-team vs per-env, image tagging, resource naming, which registry is theirs
  • Platform — cluster count and sizing, node classes (spot vs on-demand), CNI, CSI driver, multi-AZ posture, accelerator families (gpu.md)
  • Safety posture — how proactively to raise hardening, PDB, and cost findings versus only on request; whether destructive commands are emitted at all
  • Observability — metrics stack (Prometheus, Datadog, cloud-native), log pipeline, whether kubectl top even works, where events are shipped
  • Secrets management — details beyond secrets_backend: rotation expectations, which store holds which class of secret (the choice, never the credentials)
  • Cadence — upgrade window, drain policy, restore-drill frequency, when maintenance may touch running workloads. Anything with a date lands in the ## Due table of memory.md

Signals that fill these keys without asking — evidence the user already handed you beats a question:

Signal in their pasteInfer
eks.amazonaws.com annotations, gp3 StorageClass, alb ingress classcluster_flavor: eks
gke- node names, standard-rwo, NEG annotationscluster_flavor: gke
k3s/traefik default ingress, single nodecluster_flavor: k3s
Chart.yaml, values.yaml, {{ .Valuesmanifest_tool: helm
kustomization.yaml, an overlays directorymanifest_tool: kustomize
argocd.argoproj.io/ or kustomize.toolkit.fluxcd.io/ annotationsgitops_controller, and never hand-edit live objects (manifests.md)
pod-security.kubernetes.io/enforce: restricted on the namespacepsa_level: restricted
SealedSecret, ExternalSecret, or secrets-store.csi.k8s.io objectssecrets_backend
Anything elseNothing. A wrong inference stored silently is worse than no config

Record an inference only once the work proves it right. A preference the user states outright is a declaration and goes to config.yaml immediately, without ceremony; what you merely observed goes to memory.md.

Traps

TrapWhy it failsDo instead
memory: 512mm = millibytes → a limit of 0.512 bytes, instant OOM512Mi
ConfigMap mounted with subPathsubPath binds the file inode once; the kubelet's symlink-swap update works only at directory level, so updates never arriveMount the whole ConfigMap as a directory, or env vars + rollout restart
Liveness probe checks a dependencyDependency outage → every pod fails liveness → cluster-wide restart storm on top of the outageLiveness = in-process only; readiness owns dependencies
Keeping timeoutSeconds: 1 (the default)One GC pause or load spike kills the pod under the exact load that needed it alive2-5s on any probe doing real work
First NetworkPolicy in a namespaceSelected pods flip to default-deny for that direction; DNS breaks first, everything else followsShip the DNS egress rule (UDP/TCP 53) in the same change
Deleting a PVC on a dynamically provisioned PVDefault reclaimPolicy: Delete destroys the data with the claimRetain on anything you cannot re-derive; verify before deleting
Force-deleting a Terminating podThe API forgets the pod while the kubelet may still be running the container — StatefulSets split-brain on the "freed" identityRemove the stuck finalizer or fix the node; force only when the node is confirmed dead
CronJob with defaultsconcurrencyPolicy: Allow piles up overlapping runs when one hangs; >100 missed schedules with no startingDeadlineSeconds stops scheduling entirelyForbid (or Replace) + startingDeadlineSeconds
PDB stricter than the replica countmaxUnavailable: 0, or minAvailable = replicas, blocks every node drain and hangs cluster upgradesLeave ≥1 pod of slack, or accept the pause consciously
Changing a Deployment's spec.selectorThe field is immutable; the apply fails, and label edits without it fail validationFix the label scheme before first apply; otherwise create a new Deployment and shift traffic
Unbounded emptyDirIt consumes node ephemeral storage; the node hits DiskPressure and evicts your own podsizeLimit on every emptyDir, or a real volume
hostPort or hostNetwork for convenienceOne pod per node per port, silent Pending on the next replica, and it bypasses NetworkPolicyService + Ingress; hostNetwork only for genuine node agents (daemonsets.md)
Two Services selecting the same podsBoth work, both look correct, and traffic splits in ways no dashboard showsOne selector per workload; check with kubectl get svc -o wide before adding
Debugging from latest logsYou cannot know which code produced them (→ Core Rule 8)Pin tags; log the image digest at startup
Re-measuring what a previous session already measuredPeak memory, real drain time and RTO cost a full traffic cycle each; nobody budgets for them twiceRead ## Workloads before sizing; write the number back the moment it lands (Core Rule 10)
A backup nobody has restoredSnapshots in the same storage system, etcd snapshots without PV data, a namespace whose Secrets were never in GitRehearse the restore, then write the measured RTO into deploys/<year>.md (backup.md)

Where Experts Disagree

  • CPU limits. One camp drops them entirely: CFS throttling adds tail latency even at low average usage, and requests already guarantee a fair share under contention. The other keeps limits = requests for predictability and multi-tenant fairness. Frontier: trusted workloads on dedicated clusters → requests only; multi-tenant platforms, chargeback, or compliance regimes → limits on.
  • Liveness probes. Default-off argues most apps never deadlock without crashing, so liveness only adds restart storms and hides bugs; default-on wants a self-healing floor. Frontier: add liveness only when the process demonstrably hangs without exiting, and the probe reads purely in-process state — otherwise readiness alone.
  • Databases in the cluster. Operators have made in-cluster Postgres and Kafka genuinely viable; the counter-argument is that storage failure modes, backups, and upgrades are where the operator's maturity gets tested during your outage. Frontier: managed service unless you already run a platform team that practices restores (stateful.md).
  • Helm vs kustomize vs plain YAML. Helm wins at distributing software to strangers; kustomize wins at your own manifests across environments; plain YAML wins below roughly a dozen objects. Templating a chart for a single cluster you own is a cost with no buyer.
  • Cluster granularity. One large multi-tenant cluster maximizes bin-packing and minimizes control-plane cost; clusters per team or per environment maximize blast-radius isolation and upgrade freedom. Frontier: the moment one team's admission webhook or CRD upgrade can break another team, isolation is cheaper than the incident.
  • Service mesh. One camp treats mTLS, retries, and traffic splitting as platform baseline; the other points out that every request now traverses two extra proxies whose failure modes the app team cannot debug. Frontier: below roughly a dozen services with no compliance requirement for in-cluster encryption, the mesh costs more than it returns (mesh.md).

Related Skills

More Clawic skills, get them at https://clawic.com/skills/k8s (install if the user confirms):

  • docker — building and hardening the images k8s runs
  • devops — CI/CD pipelines that deploy to the cluster
  • incident-response — coordinating the humans when the cluster page fires
  • observability — metrics, logs, and traces behind the dashboards you read here
  • linux — node-level debugging under the kubelet

Feedback

Part of Clawic, the verified skill library. Get this skill: https://clawic.com/skills/k8s.

Top skills in this category