Cases4 cases
1 to 4 of 4
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.
- Symptoms
Standard alerting paged Honeycomb's on-call engineers about the performance and stability of event ingestion, and the automated load-shedding mechanisms tripped and sent further notifications. The status page described a higher than normal error rate and response time on the ingestion service.
- Trigger
An abnormally large number of queries arrived from many separate connections within a few minutes.
- Root cause
- Component
- Contributing factor
- Detection
Standard alerting notified the on-call engineers about ingestion performance and stability, and the automated load-shedding mechanisms added notifications when they tripped. The first status post, about alerts on a higher than normal error rate and response time, went up at 18:28 PST (02:28 UTC), 13 minutes after the 02:15 UTC start.
- Remediation
- Scaled the ingest fleet up manually and aggressively
- Cut off most ingest for a few minutes so the ingest hosts could come back with enough capacity
- Temporarily lowered the rate limits for the most aggressive traffic sources until their traffic subsided
- Action items
- Keep the ingest fleet scaled up to prevent a repeat while longer-term fixes are worked on
- Decide on the projects that will address these failure paths
- Lessons
Load shedding and auto-scaling were in place, yet a burst that arrived faster than the cache could spread its writes still took the ingest hosts down. During recovery, Kubernetes' crash loop back-off worked against the operators: the hosts that had failed stayed offline while the fleet needed every host. Clients that retry whole OTel batches after a partial drop can multiply traffic many times over, so per-source rate limits were what finally stabilised ingest.
IL-0194After a kube-apiserver restart, pod LIST requests from every pod of a Vector DaemonSet nearly tripled etcd memory and restarted all master nodesPalark2023- Symptoms
One evening, right after an ordinary restart of kube-apiserver in a customer's production cluster, etcd memory consumption rose almost threefold. The sudden growth led to a cascading restart of all the master nodes.
- Trigger
An ordinary restart of kube-apiserver, after which every pod of the log collector DaemonSet sent its list requests at the same time.
- Root cause
- Component
- Contributing factor
- Detection
Memory graphs showed etcd memory on the master nodes almost tripling after a kube-apiserver restart; the audit logs then pointed to one DaemonSet's list requests.
- Remediation
- created a PriorityLevelConfiguration named limit-list-custom that queues requests (assuredConcurrencyShares 5, 16 queues, handSize 4, queueLengthLimit 50)
- created a FlowSchema that routes list and get requests on pods from the log collector's service account to that priority level
- Action items
- Submit a pull request adding resource version support to the Kubernetes Rust library
- Open a pull request to the Vector log collector to address the startup list requests
- Apply the same flow-control approach to a similar problem found with the Cilium CNI
- Lessons
A field selector reduces what kube-apiserver returns, not what it reads from etcd: listing the pods of one node still reads every pod in the cluster. A DaemonSet whose pods all list at startup multiplies that by the node count each time kube-apiserver restarts. Where exact freshness is not needed, resourceVersion=0 lets kube-apiserver answer from its cache; where the client cannot set it, API Priority and Fairness can queue the requests so they reach etcd gradually.
IL-0206Consul agents in GitLab.com's GKE pods were repeatedly OOM-killed, so the Rails pods lost database replica discovery and sent every query to the primaryGitLab2022- Symptoms
On 2022-04-25 the Sidekiq Apdex (the share of background jobs that finish within a target time) dropped, and an engineer declared an incident in Slack at 15:13 UTC. Users saw intermittent slowness across GitLab.com, and asynchronous work such as pipeline creation and import and export took unusually long.
- Trigger
The Consul agent in GitLab.com's GKE pods had memory demand spikes that did not fit in the pods' 500MB memory limit, and the agent was killed again and again.
- Root cause
- Component
- Contributing factor
- –
- Detection
An engineer declared the incident in Slack at 15:13 UTC after the Sidekiq Apdex dropped; the second event was found when monitoring detected an elevated error rate at 01:21 UTC on 2022-04-26.
- Remediation
- planned at 16:13 to raise the per-user connection limit for the gitlab database user and the pgbouncer backend pools for the main database; changes were applied at 17:26 and confirmed at 17:47
- raised the pod memory limits from 500MB to 1GB so the Consul agents survive memory spikes
- Action items
- Monitor the Consul agents on GKE pods to see whether the 1GB memory limit keeps them healthy
- Investigate the Consul memory demand spikes and what can be done beyond raising the hard memory limit
- Lessons
When the local Consul agent that Rails uses for database service discovery keeps dying, Rails does not fail; it sends every query to the last known primary. What engineers saw was database saturation and slow background jobs, which pointed the investigation first at a recent pgbouncer pool change. The memory limit of the Consul agent in every pod therefore decided the load on the primary database.
These 4 cases involve Application database and In-memory cache. 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 exhaustion4 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-0202A burst of queries and database writes exhausted connection pools and memory on Honeycomb's ingest hosts, and Kubernetes crash-loop back-off kept failed hosts offline so ingest capacity stayed too low to recoverHoneycomb2023IL-0194After a kube-apiserver restart, pod LIST requests from every pod of a Vector DaemonSet nearly tripled etcd memory and restarted all master nodesPalark2023IL-0206Consul agents in GitLab.com's GKE pods were repeatedly OOM-killed, so the Rails pods lost database replica discovery and sent every query to the primaryGitLab2022