Back to portfolio
Case study · Platform networking

Migrating GKE to Dataplane V2 (Cilium / eBPF)

GKE is switching its default networking from iptables to Cilium and eBPF, and the field that controls it cannot be changed on a running cluster. Here is how I turned a forced destroy-and-recreate into a versioned GitOps bootstrap and an 18-point validation plan.

By Vitor Schiavo Updated October 2026 ~7 min read
Solution at a glanceChange the network in a controlled environment.
Existing clusterCapture a working baseline
Dataplane V2Apply network changes
ValidationTest paths and rollback

Non-production delivery with explicit checks and rollback; production was outside this scope.

Contribution at a glance

My responsibility
Non-production network migration and repeatable platform bootstrap.
Scope
Development, staging and sandbox migration; production excluded from this delivery.
Collaboration & boundaries
I validated infrastructure and controller compatibility in the available non-production window.
Outcome
A staged non-production delivery with explicit validation and rollback; no production-wide migration result is claimed.

GKE announced that from March 30, 2027, new clusters default to Dataplane V2 (DPv2), which replaces the old kube-proxy and iptables data plane (Dataplane V1) with Cilium and eBPF. The catch: the datapath_provider field is immutable. You cannot flip a running cluster from V1 to V2. The only way across is to destroy and recreate the cluster.

Why move early, not at the deadline

Waiting until 2027 is the expensive option. I moved while it was cheap and safe:

Making the immutable field explicit

The root cause of the "surprise in 2027" risk is that the field had always been left to the cloud default. I made datapath_provider an explicit, required Terraform variable on the cluster module, so no cluster is ever created without a conscious choice:

The lesson generalizes: any immutable infrastructure field should be an explicit variable in code, never an inherited default. Defaults change out from under you.

The silver lining: a GitOps bootstrap

Before this, bringing a cluster up meant a pile of manual kubectl apply and ad-hoc Helm installs: cert-manager, two ingress controllers, the autoscaler, cluster issuers. That knowledge lived in people's heads. Because a destroy-and-recreate would have lost all of it, I converted it into a versioned bootstrap directory plus a CI workflow, so bringing a cluster up from zero is now a deterministic, reviewed pipeline run. The recreate also let me upgrade the autoscaler a few minor versions and pin exact versions of cert-manager and the rest.

Each recreated cluster then re-registers with the central GitOps controller, since the cluster's API endpoint, token and CA certificate all change on recreate.

eBPFCilium data plane
GitOpsrepeatable bootstrap
18-pointvalidation plan
0 → prodrisk staged, prod last

Validate the data plane, do not assume it

Switching the data plane can change subtle behaviour (NetworkPolicy enforcement, source IPs, connectivity). I validated each recreated cluster against an 18-point plan: NetworkPolicies, external secrets with Workload Identity, GitOps sync, Prometheus scraping, cert-manager issuance, and egress through Cloud NAT, among others. Green across the board before anything depended on it.

What I would tell someone starting this

Decisions and next steps

A retrospective view of the approach and its limits. The next improvement is a proposal, not a delivered result.

Alternative and rationale

Keeping the previous dataplane avoids immediate migration work; a controlled rebuild provides a chance to validate the replacement before a future dependency change.

Trade-off

Immutable cluster networking requires recreation. Controller compatibility and restore order matter as much as cluster creation.

What I would improve next

Next improvement: retain a compatibility checklist and rehearse production prerequisites before planning any separate production migration.