Run this against your own cluster and you might get a surprise:
echo -n "Q" | openssl s_client -connect 127.0.0.1:6443 -CAfile ca.crt 2>/dev/null \
| grep "Negotiated TLS1.3 group"
# Negotiated TLS1.3 group: X25519MLKEM768If your control plane runs Kubernetes v1.33 or newer, that X25519MLKEM768 means your kubectl session just negotiated a hybrid post-quantum key exchange — classical X25519 plus ML-KEM-768, secure as long as at least one of the two holds. Nobody flipped a flag. Nobody read a migration guide. Go 1.24 enabled it by default, Kubernetes v1.33 picked up Go 1.24, and, as Fabian Kammel's July 2025 Kubernetes blog post put it, post-quantum KEMs arrived in Kubernetes almost by accident.
Here is the uncomfortable second half of that surprise: your API server is one TLS hop out of about a dozen in a self-hosted PaaS. The ingress terminating your tenants' custom domains, the mesh mTLS between their services, the admission webhooks, the registry, the client libraries — each runs its own TLS stack, with its own defaults, on its own upgrade clock. Some are already quantum-safe. Some need a one-line config change. And the certificate half of the problem is blocked upstream almost everywhere.
That is why the practical near-term task is not an algorithm switch. It is an inventory: trace every TLS hop, learn which ones can adopt hybrid cryptography today, and rehearse rotations before a compliance deadline turns the migration into an emergency cutover. This post is that playbook for a Cluster-API-managed fleet.
The deadlines have dates now
The "someday" framing expired. Three timelines now pin the migration down:
| Source | Milestone | Date |
|---|---|---|
| NIST IR 8547 | RSA-2048 / ECC P-256 deprecated | 2030 |
| NSA CNSA 2.0 | New national-security acquisitions must support PQC | Jan 2027 |
| NSA CNSA 2.0 | Software/firmware signing, networking equipment exclusive PQC | 2030 |
| NSA CNSA 2.0 | Web, cloud, operating systems exclusive PQC | 2033 |
| NIST IR 8547 | Quantum-vulnerable algorithms disallowed | 2035 |
NIST finalized its first three standards — ML-KEM (FIPS 203), ML-DSA (FIPS 204), SLH-DSA (FIPS 205) — in August 2024, selected HQC as a backup KEM in March 2025, and the web has moved fast since: Chrome enabled the hybrid X25519MLKEM768 exchange by default in April 2024 (standardized form in Chrome 131), Firefox and OpenSSL 3.5 followed in early 2025, Apple shipped it across its OS 26 releases, and Cloudflare reported more than two-thirds of human-generated TLS traffic negotiating hybrid post-quantum by spring 2026.
Even if your tenants never touch a regulated workload, the threat model that matters is already active: harvest now, decrypt later. An adversary recording encrypted traffic today can store it and decrypt it once a capable quantum computer exists. Key exchange is the urgent half of TLS precisely because today's handshakes protect today's bytes retroactively. Signatures are different — and that difference is the whole migration strategy.
KEMs now, signatures later
TLS uses public-key cryptography for two jobs with opposite urgency:
| Key exchange (KEM) | Signatures (certificates) | |
|---|---|---|
| Protects | Every byte of the session, forever | Server authenticity, at connect time |
| Quantum urgency | Immediate — recorded traffic decrypts later | Lower — trust can't be abused after the fact |
| PQC answer | Hybrid X25519MLKEM768, shippable today | ML-DSA / SLH-DSA, still landing in toolchains |
| Cost of switching | ~1.2 KB keys, occasional packet-size gotchas | 12x certificates, slower verification, root-CA upgrades |
This split is why the Kubernetes story starts with key exchange and why your playbook should too. Phase one — hybrid KEMs on every hop — is mostly upgrades and config. Phase two — post-quantum signatures — is waiting on Go 1.27's crypto/mldsa, cert-manager support, and an ecosystem of issuers that can sign ML-DSA at all. Conflating the two is how teams either panic ("nothing supports PQC!") or get complacent ("our API server does it already!"). Both are half true.
The inventory: every TLS hop in your PaaS
Here is the core artifact. Walk your own fleet and fill in the last column — the point is that no two hops share a fate:
| # | Hop | Typical terminator | Typical cert lifetime | PQC status today |
|---|---|---|---|---|
| 1 | Tenant custom-domain ingress | Gateway API / Ingress on Envoy or NGINX | 90 days (ACME) | KEM: yes with config (Envoy) or OpenSSL 3.5 default (NGINX); sigs: classical |
| 2 | Certificate issuance | cert-manager issuers (ACME, private CA, Vault) | Issuer-dependent | Blocked on ML-DSA issuer support; design proposal in flight |
| 3 | API server serving + aggregation | kube-apiserver (Go) | 1 year (kubeadm default) | KEM: yes by default on v1.33+; sigs: classical |
| 4 | Kubelet + node bootstrap | kubelet, CSR flow (Go) | 1 year, auto-rotated | KEM: yes by default on v1.33+; sigs: classical |
| 5 | etcd peer + client | etcd (Go) | 1 year typical | KEM: follows Go toolchain version; verify per release |
| 6 | Admission + API webhooks | Your controllers (Go/Rust/Python) | Often 1 year, frequently forgotten | KEM: depends on each webhook's TLS library; audit individually |
| 7 | Mesh mTLS east-west | Istio/Linkerd sidecars or ambient | 24 h (Istio/Linkerd default) | Linkerd 2.19+: ML-KEM-768 by default; Istio: group enabled, verify config |
| 8 | Registry + artifact pulls | Registry, kubelet/containerd pulls | 90 days–1 year | KEM: depends on registry TLS stack; often OpenSSL-based, check version |
| 9 | Clients: kubectl, CI, agents | kubectl, client-go, language SDKs | kubeconfig creds, often years | KEM: yes if built with Go 1.24+; audit agent SDKs individually |
Two things jump out. First, hops 3 and 4 — the ones Kubernetes owns directly — are the free wins, and they arrived silently. Second, hop 6 is where self-hosted platforms quietly fail: every admission webhook you or your add-ons run is its own TLS server with its own library, and "the cluster is on v1.34" says nothing about the Python webhook someone installed two years ago.
On a Cluster-API fleet, run this inventory per machine pool, not once per fleet. Node images bake in a Go toolchain (for kubelet), a container runtime, and CNI binaries; a pool whose template still references last quarter's image can lag the control plane by a full KEM generation. Record the CAPI provider, Kubernetes version, and node-image revision next to each pool's row, and treat template skew the same way you treat version skew: as a finding, not background noise.
What's already free — and the two gotchas
The free wins come from three defaults. Go's crypto/tls has offered X25519MLKEM768 by default since 1.24 whenever CurvePreferences is unset — which is the case for the API server and kubelet. OpenSSL 3.5, released April 2025 and supported through April 2030, offers and prefers the hybrid group by default, which quietly upgrades every NGINX, HAProxy, registry, and Python-requests hop linked against it. And every modern browser already speaks it, so tenant-facing traffic from real users negotiates hybrid the moment your ingress does.
Before you declare victory, check the two traps Kammel's post flagged:
Gotcha 1: the Go 1.23/1.24 draft mismatch. Go 1.23 shipped experimental support for a draft Kyber variant (X25519Kyber768Draft00); Go 1.24 replaced it with the finalized X25519MLKEM768. Kubernetes v1.32 builds on Go 1.23, v1.33 on Go 1.24 — so a fresh kubectl talking to a not-yet-upgraded API server shares no PQC group and silently falls back to classical X25519. The connection still works; the quantum safety quietly evaporates. During any fleet upgrade window, verify the negotiated group on both sides instead of assuming the newer binary carried its protection with it.
Gotcha 2: the 1,500-byte ClientHello. ML-KEM-768 public keys run about 1.2 KB, which can push the initial TLS ClientHello past a single Ethernet frame. Most stacks fragment and reassemble without drama, but middleboxes, older load balancers, and packet-assuming parsers in the Kubernetes networking path have all choked on it at various points — the failure catalog at tldr.fail exists because of this. If hybrid handshakes fail through one specific hop while working everywhere else, suspect the network path before the crypto.
Mesh and Gateway: the hop that decides your tenant story
For a PaaS, the highest-leverage hop is #7. Tenants don't see your API server's cipher suite; they live inside mesh mTLS and Gateway-terminated TLS. And here the defaults diverge sharply:
- Envoy (and Gateway API implementations built on it) supports
X25519MLKEM768through BoringSSL, but the group is not in Envoy's default ECDH curves. You opt in viaTlsParametersin the downstream (and upstream) TLS context; peers without ML-KEM fall back to X25519 or P-256 through normal negotiation. If you run Envoy Gateway, Contour, or Istio's Envoy data plane and never set this, your Gateway hop is classical today. - Istio enabled the group in its supported-groups list in March 2025 (upstream PR istio/istio#55589), riding the same BoringSSL support. Ambient mode's ztunnel inherits the story, but "supported" is not "negotiated" — confirm with a capture, not the release notes.
- Linkerd 2.19 (October 2025) went furthest: it rebuilt its proxy crypto on
aws-lcand made ML-KEM-768 hybrid key exchange plus AES-128-GCM the default for all meshed-pod traffic. A Linkerd mesh upgraded past 2.19 is the closest thing in the ecosystem to a hop you can check off without touching config.
Gateway API itself is neutral here — it configures listeners and certificates, while the data plane underneath decides groups. That is actually good news for crypto-agility: converge your Gateway implementations on one data plane whose PQC story you have verified, and the whole tenant-facing edge moves as one.
Signatures: the blocked half, and where to pilot first
Certificates are where the bill comes due. ML-DSA public keys and signatures are measured in kilobytes, not bytes (FIPS 204, Table 1):
| Parameter set | Public key | Signature | Rough classical equivalent |
|---|---|---|---|
| ML-DSA-44 | 1,312 bytes | 2,420 bytes | AES-128 strength |
| ML-DSA-65 | 1,952 bytes | 3,309 bytes | AES-192 strength |
| ML-DSA-87 | 2,592 bytes | 4,627 bytes | AES-256 strength |
Compare an Ed25519 public key at 32 bytes with a 64-byte signature, and you see why Kammel's post cites certificates growing roughly 12x and verification running up to orders of magnitude slower. Every oversized certificate crosses the wire on every handshake, lands in every in-memory session cache, and has to fit through the same middleboxes that already dislike fat ClientHellos. The industry is standardizing hybrid certificates that pair a classical and a PQC signature, but formats, CA support, and client validation are all still converging.
The toolchain timeline, though, finally has momentum. Go 1.27 (expected August/September 2026) ships crypto/mldsa with crypto/x509 and TLS 1.3 signature-scheme support — the unblock everything downstream was waiting for. cert-manager has a design proposal for ML-DSA private keys (spec.privateKey.algorithm: MLDSA, sizes 44/65/87, defaulting to 44) building directly on that standard library. NIST is running a second standardization round for signatures with friendlier size profiles.
Case study: SPIFFE/SPIRE as the hybrid-cert pilot lane
When hybrid certificates become issuable, the wrong place to start is your year-long root or your public ACME chain. The right place is SPIFFE SVIDs — and the reason is lifetimes. SVIDs rotate automatically, often hourly, through an API your workloads already consume; a bad PQC experiment costs you one rotation window, not a root ceremony. Identity stays decoupled from transport, so you can trial hybrid SVIDs on a slice of workloads while the mesh's TLS sessions keep negotiating whatever the data plane supports.
The honest caveat: SPIRE's own PQC certificate support is still an upstream tracking item (spiffe/spire#6975), not a shipped feature. Treat the SVID lane as reserved runway — wire your identity consumption through SPIFFE now, keep rotation fully automated, and the day hybrid issuance lands you have the one certificate population in your fleet that can cut over in hours instead of quarters. Short-lived, API-issued, automatically rotated credentials are the shape every certificate in your fleet should be moving toward anyway; PQC just gives the argument a deadline.
The 4-phase playbook
Ordered by payoff per unit of risk. Do not skip phases — each one is the safety net for the next.
Phase 0 — Inventory (this week). Fill in the nine-hop table for every machine pool. Record TLS library and version per hop, negotiated group per hop (capture it; don't trust release notes), issuer per certificate population, and lifetime per population. You now own a cryptographic bill of materials — the artifact every auditor, and CNSA 2.0's 2027 procurement gate, will ask for first.
Phase 1 — KEMs everywhere (this quarter). Upgrade control planes and node images so every Go hop runs a toolchain with finalized ML-KEM; set Envoy TlsParameters to prefer X25519MLKEM768; upgrade OpenSSL-linked components to 3.5+; re-verify negotiated groups hop by hop, watching for the Go draft/final downgrade and ClientHello fragmentation through older load balancers. Success criterion: every hop you control negotiates hybrid, and you have captures proving it.
Phase 2 — Hybrid-cert pilot (when issuers land). Start with the shortest-lived population — SVIDs, then mesh workload certs, then internal webhook serving certs — and only then consider longer-lived chains. Prefer hybrid (classical + PQC) certificates over pure-PQC during the transition so a lattice scare doesn't become an outage. Keep public-facing ACME chains classical until your CA and your clients' trust stores both support the new formats.
Phase 3 — Shorten lifetimes, rehearse rotations (ongoing). Crypto-agility is not a feature flag; it is the demonstrated ability to swap every certificate in the fleet fast. Drive every population toward short, automated lifetimes, then rehearse: rotate each CA and each population on a schedule, including one unplanned-fire-drill rotation per half, until a forced algorithm cutover is just another rotation. A fleet that rotates everything monthly will survive 2030's deprecation notices. A fleet with three-year webhook certs stapled to forgotten controllers will not.
Your API server got its post-quantum upgrade for free, by dependency. Everything else on the table you have to earn — but the earning is mostly inventory, upgrades, and one config line in Envoy. Start the inventory this week, while the deadlines are still polite suggestions and not procurement gates.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



