563 Things Karpenter Didn’t Know About

Gergely Daroczi
Attila Nagy

2026-10-30

563 Things
Karpenter Didn’t Know About

Gergely Daroczi & Attila Nagy, Spare Cores

Oct 30, 2026 @ Budapest, Hungary

Slides: sparecores.com/talks

Press Space or click the green arrow icons to navigate the slides ->

A couple weeks ago at an undisclosed location not too far, far away… Gergely and Attila met and among other topics they discussed how badly their Python app scales… Despite using Kubernetes…

Guess the Number!

How much better is the highest performing 2-vCPU cloud server's single-core performance compared to the lowest?

Your guesses

SERVER A SERVER B
Vendor Hyperscaler Hyperscaler
Server SKU 👻 👻
vCPUs 2 2
Architecture x86_64 x86_64
Physical cores 1 2
CPU allocation Shared Dedicated
CPU (turbo) frequency 2.2 GHz 5.0 GHz
CPU L1D cache 32 KiB 96 KiB
Memory 1 GiB 8 GiB
Single-core SCore 133 4,645
Performance ratio x34.92481

30-50x

10-20x

100x

10-100x

Alibaba Cloud Amazon Web Services Google Cloud Platform Hetzner Cloud
Microsoft Azure OVHcloud UpCloud Vultr

SELECT
  vendor_id,
  COUNT(*) AS servers
FROM server
WHERE vcpus = 2
GROUP BY 1;
vendor_id servers
alicloud 192
aws 146
azure 174
gcp 41
hcloud 8
ovh 13
upcloud 18
vultr 25

$ apropos sparecores.com

  • Open-source tools, database schemas and documentation to inspect and inventory cloud vendors and their offerings, including pricing and measured performance.
  • Managed infrastructure, databases, APIs, SDKs, and web applications to make this data publicly accessible.
  • Open-source helpers to select, start and manage instances in your own environment.
  • Open-source Python/R packages and workflow orchestration extensions (e.g. Metaflow) to track resource usage and cost of DS/ML/AI jobs. Open-source tooling to right-size instances.
  • Add-on services to scale data science workflows, even without direct vendor engagement.

$ curl -I github.com/sparecores

$ ls -1 sparecores

$ curl -I sparecores.com/navigator

Source: sparecores.com

$ curl sparecores.com/servers

$ curl sparecores.com/server/gcp/g2-standard-16

$ curl sparecores.com/server/gcp/g2-standard-16

$ curl sparecores.com/server/gcp/g2-standard-16

$ curl sparecores.com/server/gcp/g2-standard-16

$ curl sparecores.com/server/gcp/g2-standard-16

$ curl sparecores.com/server/gcp/g2-standard-16

$ curl sparecores.com/server/gcp/g2-standard-16

$ curl sparecores.com/server/gcp/g2-standard-16

>>> from navigator.www import server

$ curl sparecores.com/servers/compare

$ curl sparecores.com/servers/compare

$ curl sparecores.com/advisor

$ curl docs.sparecores.com/api

$ strace -e environment gilty.py

Before: AWS · EKS · Cluster Autoscaler + fixed node groups
→ capacity, not single-core / latency

Then: Karpenter → EKS Auto mode
→ dynamic EC2 pick under load — easy

But Karpenter AWS filters: family · cpu count · gen · …
plus instance-cpu-sustained-clock-speed-mhz — vendor clock, not a benchmark
→ how do we pick the fastest core for GILty?

PassMark ST vs catalog GHz

Same GHz, how wide is PassMark?

Within one clock band: up to about +200% PassMark ST spread

Just set the filter to 5 GHz?

Simple idea: instance-cpu-sustained-clock-speed-mhz >= 5000

r5a.large c8a.large m8azn.large
catalog GHz 2.5 4.5 5.0
PassMark ST ~1400 ~3800 ~4300
on-demand ~$0.11 ~$0.11 ~$0.21
regions ~16 ~7 ~6
  • only ~16 types · 2 families · ~1% of AWS catalog
  • ~2× the money for ~+10% PassMark vs c8a
  • tiny fallback pool → capacity / spot pain

$ claude –plan “fix problem”

Pub talk → need benchmark-ranked types in Karpenter (incl. EKS Auto)

Plan (sc-karpenter-nodepool ADRs + sequenced v1 plans):

  1. Own NodePools only — never patch user pools / no NodeOverlay
    → managed Karpenter + GitOps (Argo CD, …) — no fights over a shared object
  2. SCNodePool → one NodePool — template verbatim + selected instance-type list
  3. Emit universal keys only (instance-type · arch · capacity)
    → avoid RequirementsDrifted loops
  4. Selectors + ranking from Keeper (e.g. PassMark ST · family band)
  5. Churn control — 24h refresh · hysteresis · never empty list
  6. Go / kubebuilder — reuse Karpenter validation + provider test fakes

sc-karpenter-nodepool demo

$ strace -e environment sparecores.com

ENTRYPOINT [“sc-karpenter-nodepool”]

Great for picking types · not for inspector-style multi-cloud JIT

Limitations (vs “pod annotation → any cloud / region / AZ / SKU”):

  • not a provisioner — only writes a Karpenter NodePool (instance-type list)
  • one cloud · one cluster — regions from NodeClass / node labels, not per-pod
  • no cross-cloud / cross-region hop in one request (AWS≠GCP≠Hetzner from one Pod)
  • needs Karpenter + cloud provider already there (EKS / Auto · AKS NAP · GKE · …)
  • no VPC / image import / Flatcar join / mesh — that’s not this controller
  • Pod can pin a type (node.kubernetes.io/instance-type) — but only if it’s already in the pool
    · no “any vendor · any region · this SKU” from one annotation (≠ start.yml)

$ cat sc-karpenter-nodepool/ROADMAP.md

Ideal stack for sc-inspector (replace GH Actions + Pulumi start.yml):

  • Control plane: on-prem / cheap VMs — k3s (3 servers) · not EKS/GKE/AKS
  • Mesh: Headscale tailnet — workers need outbound only (any cloud/region)
  • Orchestration: Hatchet — plan runs · create inspector Pods · retries / timeouts
  • Provisioner: scarpenter — multi-vendor Karpenter provider + sc-keeper catalog
  • Nodes: Flatcar + Ignition · TLS bootstrap join · single-tenant · die when empty
  • Flow: Pod nodeSelector (vendor/type) → pending → cheapest offering → boot → inspect → tear down

> /review plan

$ ping sparecores.com