Cases3 cases
1 to 3 of 3
IL-0100PostHog's feature flags pods were packed onto Kubernetes nodes that were too small for their CPU requests, and CPU saturation above 90% caused three outages in ten days through Redis overload, connection pool exhaustion and slow queriesPostHog2025- Symptoms
On 21 October 2025 at 21:45 UTC a change to timeout handling was deployed to the feature flags service, and two minutes later automated monitoring reported higher error rates. The change was rolled back by 21:49, but error rates stayed high: about 38% of flag evaluation requests in the US datacenter returned errors. At 22:30 Redis metrics showed memory exhaustion on ElastiCache, at 22:35 Postgres connections spiked, and at 22:45 the team found a massive data transfer from Postgres into Redis. The main PostHog application was affected as well, because it shares the Redis instance with the flags service.
- Trigger
On 21 October a deployment meant to reduce timeout errors (PR #39821); on 24 October the deployment of IP-based rate limiting (PR #40074) that saw all traffic as coming from the load balancer's IP; on 28 October an unrelated routine deployment that rolled the feature flag pods in the US region.
- Root cause
- Component
- Contributing factor
- Detection
On 21 October automated monitoring detected higher error rates two minutes after the deploy. On 24 October no alert existed for 429 errors, and the team learned of the incident from customer reports that began at 18:10 UTC, a 62-minute detection delay. On 28 October on-call received a high error count alert. On 29 October the incident was reported with higher error rates and latency. No alert existed for CPU pressure, so the CPU saturation behind all three incidents was not seen directly.
- Remediation
- rolled back the timeout handling change within 2 minutes on 21 October, which did not stop the errors
- throttled connections and Redis writes on 21 October
- halted the rollout on 28 October, scaled the US flags pods to zero until Redis recovered, then scaled them back gradually
- rolled back hardware changes on 30 October at 00:03 UTC, which removed most errors but not the latency
- increased the pod resource requests and adjusted the Kubernetes fleet size so fewer pods ran on each node
- added retry limits to Envoy between the load balancer and the service
- moved database connection timeouts and other critical settings to runtime configuration and raised the timeout values
- added per-node and per-pod CPU alerts at 80% sustained for 5 minutes that page on-call
- Action items
- Form a strike team from the flags, ingestion and infrastructure teams to review the application and infrastructure for remaining bottlenecks
- Investigate separating the flags Redis instance from the application Redis instance
- Before re-enabling ArgoCD sync, align the flags service chart configuration with the values in ArgoCD and define the deployment and Redis strategy
- Make the feature flags team hard code-owners for flags-related code
- Build a dashboard of key flag metrics with runbook links and update the feature flag runbooks
- Add missing alerts on existing service and infrastructure metrics
- Make readiness checks validate dependencies that degrade under load, for example by pinging the database
- Rate-limit cache population so it cannot overwhelm Redis
- Monitor connection pools with automatic throttling near exhaustion and limit concurrent connections
- Build a production-scale load testing framework
- Build progressive rollout infrastructure to limit blast radius
- Document Postgres-to-Redis data flows and write runbooks for data transfer storms
- Lessons
Nodes that are small relative to pod requests let the scheduler pack many pods on each node, and the resulting CPU saturation showed up three different ways: excessive parallelism, connection pool exhaustion and slow queries. Treating each symptom separately let the problem return; right-sizing the pods and nodes fixed all three. CPU must be alerted on from the start. Unbounded Envoy retries and unbounded cache population amplified the overload, and a Redis instance shared with the main application spread a flags problem to the main application. A rollback did not end the incident, because the data transfer and resource exhaustion it had started kept running.
IL-0037Image registry rate-limited a whole region after a crash-looping DaemonSet with imagePullPolicy Always pulled through three NAT instancesDatadog2019- Symptoms
Users reported that their jobs were not starting, with some sign that image pulls were involved. Many pods across the cluster were in error states related to image pulls, while applications that were already running were not affected.
- Trigger
A change to the permissions on a storage bucket that one application needed to read. The application could no longer start, and its DaemonSet, running on roughly a thousand nodes of the cluster, went into a crash loop with imagePullPolicy: Always, so every restart pulled the image again although it was already on the node.
- Root cause
- Component
- Contributing factor
- Detection
Users reported that their jobs were not starting; monitoring had not caught it first.
- Remediation
- replaced the affected NAT instances so registry traffic came from new source IPs
- reconfigured the static IPs and firewall rules so applications could reach the Cloud SQL database again
- Action items
- Add an admission webhook that rejects floating image tags, starting with latest
- Apply the image tag webhook to workload objects only, not to pods, after it blocked pods created by in-cluster controllers
- Lessons
The speakers' takeaway is to be very careful with DaemonSets in large clusters: they say they had broken everything DaemonSets depend on, including DNS, Vault and the image repository, and that it is very easy to break things when managing them by the thousand. imagePullPolicy: Always made every crash-loop restart pull the image again. Sending all registry traffic through three NAT instances put the whole region behind three source IPs, which the registry rate-limited, and changing those IPs cut the connections to a critical database reached over the internet through them. An admission webhook on pods rejects the pods that controllers in the cluster create, where no user sees the error.
IL-0064CPU-heavy pods packed onto one Google Cloud node drove it to kernel panics, and the crash moved from node to node until the site was downMoonlight2019- Symptoms
On Friday, January 18, Moonlight's API began returning occasional errors that looked like connection problems with its Redis database. The API checks Redis on every authenticated request to validate the session. At the same time the Kubernetes monitoring reported some nodes and pods as unresponsive.
- Trigger
The Kubernetes scheduler put several CPU-heavy pods on the same node, including all three replicas of the website and the server-side rendering pods.
- Root cause
- Component
- Contributing factor
- Detection
API errors that looked like Redis connectivity problems, and Kubernetes monitoring that showed nodes and pods as unresponsive. Google Cloud Support later found the link between sustained 100% CPU and kernel panics.
- Remediation
- added anti-affinity rules to all major deployments so their pods spread across nodes
- Action items
- Keep working with Google Cloud support to find and fix the cause of the node kernel panics
- Lessons
Three replicas give no fault tolerance if the scheduler places them all on one node; anti-affinity rules make the spread explicit. A node failure that comes from the workload itself moves with the workload when the scheduler reschedules it.
These 3 cases involve Application database and Pod scheduling and placement. kubegap audits a Kubernetes cluster for gaps like these, read-only, from Claude Code or Codex; its checks cite public reports like the ones in this library.
By root cause1
Capacity exhaustion3 cases
- Component
- Contributing factor
- Organisation
IL-0100PostHog's feature flags pods were packed onto Kubernetes nodes that were too small for their CPU requests, and CPU saturation above 90% caused three outages in ten days through Redis overload, connection pool exhaustion and slow queriesPostHog2025IL-0037Image registry rate-limited a whole region after a crash-looping DaemonSet with imagePullPolicy Always pulled through three NAT instancesDatadog2019IL-0064CPU-heavy pods packed onto one Google Cloud node drove it to kernel panics, and the crash moved from node to node until the site was downMoonlight2019