SRE Interview Preparation

Ace your technical interviews with flashcards and detailed STAR-format incident talk-tracks.

How to Use These Narratives

For each of the four incidents, you get the 30-second version (perfect for a high-level walkthrough response), the full STAR narrative (detailed, sequential first-person account), the underlying mental model of the system component that broke, and the likely follow-up questions interviewers will ask to test your depth.

✨ Situation & Task 🎯 Actions Taken 📈 Quantified Results 🧠 Conceptual Diagnostics
INCIDENT 01 • GKE Cluster CoreDNS Load Throttling

Production DNS Outage: CoreDNS Under Load

The 30-Second Summary

"We had a production GKE cluster where CoreDNS started silently degrading under load — not crashing, just getting slow — which showed up everywhere else as API latency spikes and microservice discovery failures for an airline operations platform serving millions of transactions a day. The dangerous part was that DNS failures like this don't throw errors — the logs look completely normal. I correlated Datadog metrics with CoreDNS query volume and found the pods were under-resourced with no autoscaling, so query throughput was being throttled. I did an emergency HPA scale-out to stop the bleeding, then codified the fix — HPA policy, cache TTL tuning, and resource limits — into Terraform so it couldn't regress. We were back to full service in under 20 minutes, cut DNS latency by 65%, and I added DNS resolution P99 as a first-class SLI so we'd never be blind to this again."

STAR Narrative

Situation: I was the senior DevOps engineer on an airline operations platform running on GKE — real-time flight, booking, and operational systems processing millions of transactions daily. One afternoon, API latency across multiple services spiked simultaneously, with no single service reporting errors — which is exactly the kind of symptom pattern that sends you down the wrong path if you don't know what you're looking for. Alerts started firing for elevated response times, but nothing pointed at an obvious culprit. No recent deploys, no config changes.

Task: I needed to find the actual root cause fast — this wasn't a "look into it tomorrow" situation, it was actively degrading the operational platform in production — and fix it in a way that wouldn't just come back the next time load spiked.

Action: My first instinct was to check node and pod health across the cluster — everything came back green, which was the first red flag, not a reassurance. A cluster that's "healthy" by every standard dashboard while services are still failing almost always means the problem is somewhere silent: DNS, or something between components rather than inside any one of them. CoreDNS specifically doesn't crash or throw obvious errors under CPU pressure — it just gets slower, and slow DNS resolution shows up as generic timeouts and latency everywhere else in the system, not as a DNS error anywhere.

I pulled up Datadog and correlated DNS query volume against the CoreDNS pods' resource usage and found query throughput climbing well past what the current pod count could handle — there was no HPA configured on CoreDNS at all, so it had a fixed capacity ceiling that request volume had simply outgrown. I confirmed this wasn't just an external connectivity issue by checking internal service-to-service DNS resolution specifically, not just whether the pods could reach the public internet — those are two different health signals, and one being fine doesn't mean the other is.

Once I had the root cause, I did an emergency kubectl scale to horizontally scale CoreDNS immediately and stop the bleeding. Then, because a manual scale-up doesn't survive a redeploy or protect you the next time load spikes, I went back and codified the real fix into our Terraform-managed manifests: a proper HPA policy on CoreDNS tied to query-rate thresholds, tuned cache TTLs so repeat lookups didn't keep re-hitting CoreDNS unnecessarily, and adjusted resource requests/limits so the pods weren't starting from an under-provisioned baseline. Last thing — I added DNS resolution latency at P99 as a first-class SLI in Datadog with proactive alerting on query-rate thresholds, because the whole incident happened in the first place due to a blind spot: nobody was watching DNS latency as its own signal.

Result: Full service was restored in under 20 minutes. DNS resolution latency dropped 65% after the fix. We hit 99.99% availability on that SLO going forward with zero recurrence over the next 12 months, and I standardized the same DNS SLO dashboard across every GKE cluster we ran, not just the one that had the incident — closing that observability gap everywhere at once instead of waiting for it to bite us again somewhere else.

🧠 Mental Model: DNS Diagnostics under CPU Pressure

  • OOMKilled vs. Evicted vs. "just slow": CoreDNS degradation under load is specifically the third: no crash, no restart, no error in the logs — just latency. If you only check for crash-style errors, you will miss this failure mode entirely.
  • Allocatable memory/CPU: A node's allocatable memory/CPU is always less than its advertised capacity — the OS, kubelet, container runtime, and system daemons all reserve a slice before any pod gets to use it. If CoreDNS is under-provisioned relative to query volume, this gap bites you first.
  • "Healthy dashboards" is not "healthy system": Node status, pod status, and CPU/memory utilization dashboards can all read green while a control-plane-adjacent component like CoreDNS is silently degrading. The diagnostic instinct has to be "what isn't being monitored."
  • Internal vs. External: Internal DNS resolution and external DNS reachability are separate health checks. A pod successfully resolving google.com tells you nothing about whether it can resolve checkout-gateway.core-banking.svc.cluster.local.

Likely Follow-up Q&A

How did you confirm it was CoreDNS and not something downstream, like the services themselves?
Because the symptom was uniform across unrelated services with no shared code path — that pattern points away from application logic and toward shared infrastructure. I checked node/pod health first specifically to rule that out (all green), then went to the layer underneath application health: DNS. Correlating the timing of the latency spike against CoreDNS query volume and CPU throttling metrics in Datadog was the piece that confirmed it, not just having a hunch.
Why not just permanently give CoreDNS way more resources instead of HPA?
Static over-provisioning wastes spend most of the time and still has a ceiling — if traffic grows past that ceiling later, you're back in the same incident. HPA scales with actual query volume, so it handles both today's load and future growth without needing another manual intervention.
What's the actual tradeoff with cache TTL tuning?
Longer TTLs mean fewer repeat queries hitting CoreDNS (less load, faster average resolution), but it also means a changed record (e.g., a pod IP after a rollout) takes longer to propagate to callers still holding a cached answer. You tune it based on how often your internal service endpoints actually change versus how much load you're trying to take off CoreDNS.
Grounding Note: This aligns with GKE DNS metrics troubleshooting. The CoreDNS load throttling teach-point is a real-world Kubernetes failure mode. The HPA policy configurations and cache tuning are standard, correct remediation models.
INCIDENT 02 • Observability Silent Blackout

Observability Blackout: Log Ingestion Failure (SEV-1)

The 30-Second Summary

"Our centralized logging pipeline silently failed across an entire GKE cluster — teams lost log visibility in the middle of debugging live production issues, which is the worst possible time to also be blind. I owned restoring it end-to-end: validated every layer independently — app stdout, the FluentBit DaemonSet, the aggregation layer, Datadog ingestion — and traced it to a broken TCP connection in the FluentBit forward plugin that was silently dropping events while every dashboard still looked healthy. I reconfigured the forward plugin with retry logic and buffer tuning, restored the pipeline without touching any running workloads, and then put synthetic log injection with assertion monitors in place so we'd get an alert the instant this happened again instead of discovering it mid-incident."

STAR Narrative

Situation: In the middle of investigating an unrelated production issue, my team realized we had no logs — anywhere. The centralized logging pipeline across the entire GKE cluster had gone silent. This is about the worst timing a failure like this can have: we needed logs specifically because something else was going on, and now we were debugging blind on two fronts at once.

Task: Restore full log visibility as fast as possible, and — separately — make sure this specific kind of silent failure couldn't happen again undetected, since the scariest part wasn't that logging broke, it was that nothing told us it had.

Action: I treated this as a pipeline with distinct stages and checked each one independently rather than guessing: application stdout, the Fluentd/FluentBit DaemonSet running on every node, the aggregation layer, and Datadog log ingestion itself. Using kubectl logs against the DaemonSet pods and FluentBit's own internal metrics, I found the actual break: a TCP connection in the forwarding pipeline had failed, and it was silently dropping events on the floor instead of erroring loudly — from the infrastructure's point of view, everything still looked healthy, because the DaemonSet pods themselves were running fine; they just weren't successfully shipping anything downstream. That's the trap with this class of failure — a broken forwarding connection doesn't crash the pod holding it, so standard health checks don't catch it.

I reconfigured the FluentBit forward plugin with proper retry logic and buffer tuning to restore the connection and handle transient failures going forward, and I did it without restarting any actual workloads — the fix lived entirely in the logging layer, so there was no reason to touch anything running application traffic. Once the pipeline was back, I didn't stop there — I built end-to-end validation using synthetic log injection with assertion monitors in Datadog, so the system would actively confirm the pipeline was working by injecting a known test log and verifying it arrived, rather than just assuming health from the absence of errors.

Result: Full log visibility was restored across all services within 35 minutes. Because we now had reliable, always-on observability going forward, MTTR on subsequent incidents dropped by 40% — teams weren't losing time re-discovering "wait, do we even have logs right now" mid-incident anymore. And the blind-debugging scenario itself was permanently eliminated, since log pipeline health became a continuously monitored first-class signal instead of something we only found out about when it had already failed.

🧠 Mental Model: Pipeline Audits vs. DaemonSet Lifeline

  • DaemonSet status vs. process health: FluentBit can be Running and green on every dashboard while its actual forwarding connection is broken and silently dropping data. Pod health and pipeline health are different questions.
  • Stage-by-stage pipeline verification: App stdout → node DaemonSet → aggregation layer → destination ingestion. Validate each stage independently.
  • Synthetic validation: You cannot wait for an error message to alert you if the failure mechanism suppresses errors. You must actively inject a known signal and assert its receipt on the other side.

Likely Follow-up Q&A

How did you know it was the forwarding connection specifically, and not the Datadog side?
I isolated it stage by stage — confirmed the application was writing to stdout normally, confirmed the DaemonSet pod itself was up and running, then checked FluentBit's own internal metrics for its forward plugin, which is where I found the broken TCP connection and dropped-event counters climbing. If I'd started from "Datadog isn't showing logs" and worked backward without checking each intermediate stage, I could easily have chased the wrong layer.
Why didn't the broken TCP connection trigger a crash alert?
A dropped forwarding connection in the plugin doesn't crash the process holding it, so from a basic liveness/health-check perspective, nothing looked wrong. That's precisely why I moved to synthetic injection afterward: passive "did it error" monitoring isn't enough for a failure class that doesn't produce errors.
Grounding Note: The logging forward-plugin mechanics, layer-by-layer validation, and synthetic telemetry indicators represent standard, correct practices for central logging clusters.
INCIDENT 03 • EKS Control Plane Admission Freeze

Kubernetes Control Plane Freeze: Admission Webhook Deadlock

The 30-Second Summary

"We had an EKS cluster hit a partial freeze — deployments stuck in Pending or Terminating, blocking release pipelines for multiple teams — while every monitoring dashboard showed the cluster as healthy. That's the dangerous part: a 'green illusion,' where standard health checks give you false confidence. I compared kubectl cluster-info and pod states across control-plane nodes directly, which surfaced inconsistencies the dashboards weren't catching, then went into the API server's audit logs and found admission webhook calls timing out at exactly 5 seconds, which was queuing up write requests and effectively deadlocking the controller-manager. I patched the webhook's timeout and failure policy, confirmed etcd latency normalized, and then instrumented webhook latency as its own Prometheus metric with Grafana alerting so this specific blind spot could never mask a deadlock again."

STAR Narrative

Situation: On an EKS cluster running critical product workloads, deployments started getting stuck — some in Pending, some in Terminating — and this wasn't isolated to one team's service, it was blocking release pipelines across multiple product teams simultaneously. The worst part: every monitoring dashboard we had said the cluster was healthy. Nodes green, standard metrics green — what I've since come to think of as the "green illusion," where the dashboards you'd normally trust are exactly what's giving you false confidence.

Task: Figure out what was actually happening at the control-plane level — since dashboards were useless here — restore the ability to deploy, and close whatever monitoring gap let this go undetected in the first place.

Action: Because the symptom was deployments stuck rather than any application misbehaving, I treated this as a control-plane problem from the start, not a data-plane one — that distinction matters, because it tells you where to even start looking. I compared kubectl cluster-info output and pod states directly across the control-plane components, which is what actually surfaced inconsistencies that the aggregated dashboards were smoothing over — individual component state told a different story than the summary view did. From there I went into the API server's own audit logs, and that's where I found it: admission webhook calls were consistently timing out at exactly 5 seconds. Every write request that had to pass through that webhook was queuing up waiting on a timeout that kept happening, and that queuing was enough to effectively deadlock the controller-manager — it wasn't crashed, it was just permanently backed up behind requests that would never complete in time.

I patched the admission webhook's timeout configuration and its failure policy so a slow or failing webhook call couldn't block the entire write path going forward, then verified etcd latency normalized back to baseline as confirmation the fix actually resolved the underlying pressure, not just the symptom. I also cross-checked Jenkins pipeline logs specifically to confirm the blocked deployments were a downstream consequence of this control-plane state, not a separate application-config problem that happened to be coinciding with it — you don't want to fix one thing and assume it explains everything without checking. Last step: I instrumented admission webhook latency as its own dedicated Prometheus metric with a Grafana alerting threshold, because the entire incident was possible in the first place due to a monitoring blind spot — control-plane health summary dashboards existed, but nothing was watching webhook latency specifically, which is exactly the layer that actually broke.

Result: Cluster write operations and deployment pipelines were restored within 45 minutes. The "green illusion" itself got eliminated as a risk going forward — control-plane health, specifically at the webhook layer, is now a first-class monitored metric, not something inferred from a summary dashboard. We introduced a formal admission webhook SLO off the back of this, and had zero similar incidents in the following 18 months.

🧠 Mental Model: Control Plane Write Paths & Webhook Blockers

  • Control Plane vs. Data Plane: Deployments stuck in Pending/Terminating with healthy nodes and networks points squarely at the control plane (scheduler, controller-manager, etcd, API Server).
  • Webhook interceptors: Admission webhooks sit directly in the write path. Every object creation/update matching the webhook rules must wait for its response. A slow webhook bottlenecks all API write traffic.
  • The Green Illusion: Standard metrics dashboards smooth over system component errors, masking localized control bottlenecks. Always check individual component states.

Likely Follow-up Q&A

Why would a webhook timeout deadlock the controller-manager?
Because the failure policy on the webhook determines what happens when it doesn't respond in time. If it's not configured to fail open (or to have a short-enough timeout relative to the volume of requests hitting it), every write that needs that webhook's response queues up waiting. If requests arrive faster than they time out, the write-path queue blocks completely, mimicking a controller deadlock.
What failurePolicy did you apply to resolve the deadlock?
I changed the webhook's failurePolicy from Fail to Ignore for non-critical validation, and shortened the timeout limit. This ensures transient network hiccups in non-security-critical webhooks do not bring down cluster write APIs.
Grounding Note: Webhook timeouts and failure policies in EKS clusters represent classic control-plane diagnostics taught in advanced Kubernetes war rooms.
INCIDENT 04 • IAM Roles for Service Accounts Migration

IRSA Migration Causing S3 Access Failure

The 30-Second Summary

"Right after we migrated from node IAM roles to IRSA, workloads on an EKS cluster silently lost S3 access — apps were throwing permission errors even though the nodes and infrastructure were completely healthy, and it was blocking data pipeline jobs. I ran `aws sts get-caller-identity` directly inside the affected pods and confirmed the IRSA role was being assumed correctly — so it wasn't an assume-role problem — but it simply didn't have the S3 permissions the old node IAM role used to grant. IRSA takes precedence over the node role once it's in play, and the S3 policy had never actually been replicated onto the new IRSA role during the migration. I fixed the IRSA policy directly, added a policy-diff validation step to the Jenkins pipeline so this specific gap couldn't happen silently on a future role change, and tightened the node IAM role down to a minimal baseline now that workloads weren't relying on it for permissions anymore."

STAR Narrative

Situation: We'd just completed a migration from node-level IAM roles to IRSA (IAM Roles for Service Accounts) on an EKS cluster — a legitimate security improvement, since it moves from broad node-wide permissions to per-workload, least-privilege identity. Shortly after, Kubernetes workloads started silently losing S3 access. Applications were throwing permission errors, but the nodes and the rest of the infrastructure were completely healthy — nothing else was wrong, which made this look confusing at first rather than obviously IAM-related.

Task: Diagnose exactly why S3 access broke post-migration, fix it without just reverting the security improvement we'd made, and prevent this specific gap from recurring on any future IAM role change.

Action: Because the failure was specifically permission-shaped (access denied, not connectivity or infrastructure errors) and it started right after an IAM-related migration, I focused the investigation there immediately rather than treating it as a general networking or infrastructure issue. I ran aws sts get-caller-identity from directly inside an affected pod to check exactly which identity AWS believed that pod was operating as — and confirmed the IRSA role was actually being assumed correctly. That ruled out the most obvious failure mode (a broken trust relationship or OIDC misconfiguration preventing the role assumption itself) and told me the problem was specifically about what permissions that correctly-assumed role actually had.

From there, the root cause became clear: IRSA takes precedence over the node's IAM role once it's configured — that's the whole point of it, scoping permissions to the workload instead of the node — but that also means the workload no longer inherits anything from the node role anymore. During the migration, the S3 resource permissions that had previously been granted via the node role had never actually been replicated onto the new IRSA role's policy. The pods weren't failing to authenticate — they were authenticating correctly as an identity that simply didn't have the S3 access it needed.

I updated the IRSA IAM policy with the correct S3 resource permissions and validated the fix in a controlled way before re-enabling the affected production workloads, rather than just pushing the change straight to prod and hoping. Then, because this exact gap — a role migration silently missing a permission that used to be implicit — is exactly the kind of thing that's easy to reintroduce later, I added a policy-diff validation step directly into the Jenkins CI/CD pipeline to catch permission gaps automatically on any future IAM role change, instead of relying on someone remembering to check manually. Last step, once I'd confirmed workloads were fully running on IRSA and no longer depending on the node role at all, I reduced the node IAM role's permissions down to a minimal baseline — completing the actual security improvement the migration was meant to deliver in the first place, instead of leaving broad node permissions sitting around unused as leftover risk.

Result: Application access was fully restored with zero further production data-pipeline interruptions after the fix. The IAM policy-diff gate in CI/CD now catches permission regressions automatically on every future role change — this exact class of "the new role is missing something the old role implicitly had" gap can't slip through silently again. And node IAM role permissions were reduced to a true minimal baseline, which is a real security-posture improvement on top of just fixing the outage.

🧠 Mental Model: IRSA Precedence & Pod IAM Resolution

  • IRSA overrides node roles: IRSA takes absolute precedence once configure-scoped. It does not append permissions. Workloads stop inheriting node credentials entirely.
  • Caller Verification: aws sts get-caller-identity isolates trust relationship/OIDC assembly faults from policy permission authorization faults.
  • Implicit permissions gap: Node-wide roles mask application dependencies. Tightening node profiles will expose these missing links post-migration.

Likely Follow-up Q&A

How did you confirm the issue wasn't OIDC or trust relationship configuration?
Running aws sts get-caller-identity inside the pod succeeded and returned the IRSA ARN role identity. If there were OIDC or trust relationship blocks, the AWS client invocation would fail to authenticate and return an assume-role error instead.
What does the policy-diff check compare in CI?
The policy-diff gate extracts the active policies of a role in staging/prod and compares it with the terraform plan target permissions. It raises warnings if any S3, KMS, or DB resource permission path is removed or scoped down.
Grounding Note: The AWS IRSA credentials mounting process, STS identity checks, and least-privilege scoping represent standard, accurate AWS EKS IAM practices.
INITIATIVE 05 • Enterprise FinOps Cost & Security Audit

Enterprise-Wide Cloud Cost & Security Audit (FinOps Initiative)

The 30-Second Summary

"I led a systematic cost and security audit across roughly 20 separate GCP service accounts for a multi-service fintech platform, plus the AWS side of the same estate. Rather than eyeballing the bill, I built a standardized audit workbook per service — billing breakdown by GCP product, percentage contribution to spend, and a checklist-driven scan against known cost anti-patterns like missing committed-use discounts, oversized instances, and non-prod resources running 24/7. Across the full estate that added up to a bit over $2M a month in aggregate spend, and the audit identified right around a third of that — about 33% — as realistic savings, which lines up with the 25–40% range I've delivered consistently. On the AWS side I also automated non-prod start/stop scheduling with Lambda and EventBridge, and stood up Karpenter for intelligent node provisioning on EKS with an Intel-to-AMD-to-Spot migration path. And because I was already in every project's IAM and network configuration doing the cost audit, I ran a parallel security pass and surfaced real findings — publicly exposed database IPs, missing encryption on datasets — that became their own remediation backlog, not just a cost exercise."

STAR Narrative

Situation: I was brought in to reduce cloud spend across a large, multi-service fintech platform — not one application, but somewhere around 20 distinct GCP service accounts covering everything from core banking-adjacent services to reporting, analytics, and customer-facing products, plus a parallel AWS estate. Nobody had a single unified view of where the money was actually going; spend was being reviewed per-service, informally, with no standardized method for finding savings or tracking whether a recommendation had actually been implemented.

Task: Build a repeatable, defensible audit process — not a one-off spreadsheet exercise — that could be applied consistently across every service, produce real, implementable recommendations with estimated savings attached to each one, and get organizational sign-off and tracking so recommendations didn't just sit in a document nobody acted on.

Action: I designed a standardized audit workbook template and applied it to every single service account individually, rather than trying to analyze the whole estate in aggregate — aggregate numbers hide which specific service is actually driving cost. Each service's workbook broke down billing by GCP product (Cloud SQL, Compute Engine, Cloud Run, Networking, Cloud Logging, Memorystore, Security Command Center, and more), calculated what percentage of that service's total spend each product represented, and then applied a realistic potential-savings percentage per product based on a checklist of known cost anti-patterns — was compute using AMD/ARM architecture where viable, were committed-use or sustained-use discounts in place for predictable workloads, were non-prod instances actually being shut down outside working hours, was anything idle or overprovisioned sitting around unused. I tracked every check with a simple pass/fail flag per project so gaps were immediately visible at a glance across the whole estate, not buried in prose.

On the AWS side of the same estate, I built and deployed Lambda-based automation, triggered on an EventBridge schedule, to automatically stop non-prod EC2 instances outside working hours and start them back up when needed — tagged by environment so it applied consistently without needing per-instance manual configuration. For the EKS clusters specifically, I stood up Karpenter to replace static, over-provisioned node groups with intelligent, right-sized provisioning — configuring the IRSA-bound controller role, the subnet and security-group discovery tagging Karpenter needs to place nodes correctly, and a NodePool definition that let it choose the cheapest viable instance type for actual pod resource requests rather than a fixed, worst-case instance size. That fed directly into a broader Intel-to-AMD-to-Spot migration path for compute-heavy workloads, moving from on-demand Intel instances to AMD-based equivalents first, then onto Spot capacity for workloads that could tolerate interruption — each step captured as its own line item with its own estimated savings, not bundled into one vague "moved to cheaper instances" claim.

Because doing this properly meant going into every service's IAM policies, network configuration, and encryption settings anyway to understand what was actually running, I built a parallel security posture audit alongside the cost one instead of treating them as separate exercises — the access I already had made it nearly free to check for things like databases with public IPs, datasets not encrypted with customer-managed keys, and instances not requiring SSL/TLS for internal connections, and flag each finding by risk level per project. Every recommendation — cost and security both — went through an internal review and sign-off step before being marked complete, so the tracker gave leadership a real, auditable view of what had actually been implemented versus what was still pending, rather than a one-time report that went stale the moment it was delivered.

Result: Across the full audited estate, the aggregate potential-savings figure came out to right around a third of total spend — squarely inside the 25–40% range I've delivered consistently across engagements — identified through a systematic, checklist-driven process rather than ad hoc guessing, with every recommendation individually justified and tracked to sign-off rather than left as an unverified estimate. The non-prod scheduling automation and Karpenter rollout took a real, ongoing chunk of that off the table immediately and kept it off without needing manual intervention going forward. And the security pass that came along for the ride surfaced genuine, previously-unflagged risk — publicly exposed resources and missing encryption — that became its own accountable remediation track instead of quietly going unnoticed because nobody happened to be looking at that layer.

🧠 Mental Model: Checklist-Driven FinOps & Karpenter Sizing

  • A cost audit is only as trustworthy as its methodology: "We think we can save 30%" means nothing without a per-service, per-product breakdown showing exactly where that number comes from — that's the difference between a credible FinOps deliverable and a guess.
  • Non-prod scheduling and rightsizing are structural wins: A manually-stopped instance gets started again by someone forgetting to re-stop it; a Lambda/EventBridge schedule or a Karpenter-driven right-sizing policy keeps the saving in place without depending on anyone remembering to enforce it.
  • Karpenter's core value is matching real pod resource requests to real instance sizing, dynamically: Instead of a fixed node group sized for worst-case load sitting idle most of the time, Karpenter provisions (and consolidates) based on what's actually pending, and the AMD/Spot migration path is a second, compounding layer of savings on top of that right-sizing.
  • Cost and security audits share the same raw material: Anywhere you're already reviewing IAM, network exposure, and encryption settings to find cost inefficiencies, you're one pass away from a real security posture review too — treating them as one combined exercise is more efficient.

Likely Follow-up Q&A

How did you actually validate that your estimated savings percentages were realistic, rather than optimistic guesses?
Each estimate was tied to a specific, checklist-driven finding — not a blanket "cloud costs are always 30% wasteful" assumption. If a service showed non-prod instances running 24/7 with no scheduling, that had a directly calculable savings number based on actual off-hours downtime. If Cloud SQL wasn't using committed-use discounts on predictable, always-on workloads, that had a known discount percentage to apply. The estimates were as defensible as the underlying finding, and every one went through implementation and sign-off afterward, which is what actually confirms the number was real rather than theoretical.
With ~20 services to audit, how did you prioritize which ones to tackle first?
By percentage contribution to overall spend — the workbook calculated each product's share of that service's total bill specifically so the highest-impact opportunities surfaced first rather than spending equal time on a service contributing a fraction of a percent of total spend versus one driving a large share of it.
Karpenter vs. just using Cluster Autoscaler with well-tuned node groups — why the extra complexity?
Cluster Autoscaler works with pre-defined, fixed-size node groups, so you're still choosing instance types and sizes up front and living with that choice — it can add nodes from the group you defined, but it can't choose a better-fitting instance type dynamically the way Karpenter can. Karpenter provisions directly against actual pending-pod resource requests, so it can right-size per workload instead of forcing everything through one predefined shape, and it consolidates more aggressively when load drops. For a platform running dozens of services with genuinely different resource profiles, that flexibility is worth the added setup complexity.
How did the security findings actually get acted on — did that create friction with anyone owning those services?
Every finding was tracked by risk level per project and went through the same review-and-sign-off process as the cost recommendations, so it wasn't me unilaterally declaring something a problem — it was a documented, auditable finding that the relevant team then owned resolving, with the tracker giving visibility into what was actually outstanding rather than it disappearing into an email nobody actioned.
Grounding Note: This is a FinOps audit workbook and automation scheduling strategy. Karpenter dynamic node consolidation, and node architecture migrations represent standard industry FinOps guidelines.