Google Kubernetes Engine skill for Codex
Autopilot clusters that upgrade without drama. Codex treats GKE as Autopilot on a release channel by default, sets explicit pod requests because they are the bill, and grants pods GCP access through Workload Identity Federation principals instead of node service accounts. Upgrades run through maintenance windows, exclusions and a deprecated-API checklist, with production changes held for confirmation.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- gcp-gke Installs to ~/.agents/skills/gcp-gke/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install gcp-gke The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project gcp-gke Installs to .agents/skills/gcp-gke/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/gcp-gke.zipunzip -o gcp-gke.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) gcp-gke PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $gcp-gke, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Runs workloads on Google Kubernetes Engine, Autopilot first - cluster creation with release channels, pod resource requests and compute classes, Workload Identity Federation for GKE instead of node service accounts, Spot pods, PodDisruptionBudgets, maintenance windows and exclusions, and safe control-plane and node upgrades with deprecated API checks. Use when creating a GKE cluster, giving pods access to GCP APIs, planning or debugging a GKE upgrade, or fixing Autopilot pods that are rejected or stuck Pending.
Use it when
- Creating or reconfiguring a GKE cluster
- Pods need GCP API access with Workload Identity Federation
- Autopilot rejects pods or they sit Pending
- Planning or recovering from a GKE version upgrade
Use something else when
- Writing generic Deployment or Service YAML: use kubernetes-manifests
- Debugging CrashLoopBackOff or networking inside any cluster: use kubernetes-debugging
- A single stateless container would do: use gcp-cloud-run
How to check the result
A test pod using the Kubernetes service account gets a token and can reach only its intended resource, and the cluster description shows the channel, window and exclusions you set.
The mistake it prevents
Omitting resource requests on Autopilot, so every pod is billed at the default 0.5 vCPU and 2 GiB.
Done well means: the cluster is Autopilot on a release channel unless there is a written reason for Standard, pods reach GCP APIs through Workload Identity Federation with their own principals, every workload declares requests and has a PodDisruptionBudget, and upgrades happen inside a maintenance window after deprecated APIs are cleared.
Before touching a cluster
- Read
AGENTS.mdand find the source of truth: Terraform, Config Connector, gcloud scripts. Change the cluster there. - Confirm the context.
kubectlagainst the wrong cluster is the classic GKE incident:
gcloud container clusters list --project="$PROJECT"
gcloud container clusters get-credentials prod --region="$REGION" --project="$PROJECT"
kubectl config current-context
gcloud container clusters describe prod --region="$REGION" \
--format='value(autopilot.enabled,releaseChannel.channel,currentMasterVersion,workloadIdentityConfig.workloadPool)'
get-credentials needs gke-gcloud-auth-plugin installed and network access. Any change
to a production cluster (apply, delete, upgrade, drain) needs explicit user confirmation.
Autopilot or Standard
| Requirement | Choice |
|---|---|
| Default web services, workers, jobs | Autopilot |
| Privileged pods, custom kernel modules, node SSH | Standard |
| Exact node machine types for licensing | Standard, or Autopilot with a custom ComputeClass |
| GPU / TPU workloads | Either; Autopilot supports GPUs via node selectors |
| Team wants zero node operations and pay-per-pod | Autopilot |
gcloud container clusters create-auto prod --region="$REGION" --project="$PROJECT" \
--release-channel=regular --network=prod-vpc --subnetwork=gke-nodes \
--enable-private-nodes
Autopilot enables Workload Identity Federation, Shielded nodes and node auto-upgrade by
default. Choose regular for production unless the user has a reason for stable.
Requests are the bill on Autopilot
Autopilot bills on pod requests and mutates pods without them (default is roughly 0.5 vCPU and 2 GiB). Always set requests explicitly:
spec:
serviceAccountName: api
nodeSelector:
cloud.google.com/compute-class: Balanced # or Scale-Out, Performance
containers:
- name: api
image: europe-docker.pkg.dev/my-project/apps/api@sha256:...
resources:
requests: { cpu: 250m, memory: 512Mi, ephemeral-storage: 1Gi }
limits: { memory: 512Mi }
- Autopilot enforces CPU-to-memory ratios and minimums per compute class; pods outside
them are adjusted, and the warning appears in
kubectl describe pod. - Fault-tolerant batch and stateless replicas can use Spot pods:
nodeSelector: {cloud.google.com/gke-spot: "true"}plus aterminationGracePeriodSecondsof 25 or less (Spot preemption gives about 30 seconds). - Rejected pods: read the admission webhook message. Common causes are
hostPath,privileged: true,NET_ADMINand host networking, all blocked by Autopilot.
Workload Identity Federation for GKE
Grant IAM roles directly to the Kubernetes service account principal. No GCP SA, no key, no node SA permissions:
kubectl create serviceaccount api -n shop
gcloud storage buckets add-iam-policy-binding gs://shop-assets \
--role=roles/storage.objectViewer \
--member="principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$PROJECT.svc.id.goog/subject/ns/shop/sa/api"
When a GCP API does not accept the direct principal, or the team already has a GCP SA, use impersonation:
gcloud iam service-accounts add-iam-policy-binding api-gsa@"$PROJECT".iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="serviceAccount:$PROJECT.svc.id.goog[shop/api]"
kubectl annotate serviceaccount api -n shop \
iam.gke.io/gcp-service-account=api-gsa@"$PROJECT".iam.gserviceaccount.com
- The principal identifier uses the project number; the workload pool uses the project ID.
- Every pod in the same namespace using the same KSA name shares the identity, across all clusters in the project’s fleet pool. Keep namespaces meaningful.
- Remove the old node service account grants (often Editor on the default compute SA) after workloads migrate.
Test from a pod:
kubectl run wi-test -n shop --rm -it --restart=Never \
--overrides='{"spec":{"serviceAccountName":"api"}}' \
--image=google/cloud-sdk:slim -- gcloud auth print-access-token --quiet
Disruption budgets and probes
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: api, namespace: shop }
spec:
minAvailable: 2 # or maxUnavailable: 1; never a value that blocks all evictions
selector: { matchLabels: { app: api } }
GKE respects PDBs during node upgrades only for a limited time (about an hour), then
evicts anyway. A PDB that allows zero disruptions does not stop an upgrade; it only makes
it slower and less predictable. Pair PDBs with readiness probes and at least 2 replicas
spread across zones (topologySpreadConstraints on topology.kubernetes.io/zone).
Upgrades
GKE upgrades the control plane and nodes automatically on the release channel. Your job is to control when, and to make sure nothing breaks.
gcloud container get-server-config --region="$REGION" --flatten=channels \
--format='table(channels.channel,channels.defaultVersion,channels.validVersions.list())'
gcloud container clusters update prod --region="$REGION" \
--maintenance-window-start=2026-01-03T02:00:00Z --maintenance-window-end=2026-01-03T06:00:00Z \
--maintenance-window-recurrence='FREQ=WEEKLY;BYDAY=SA,SU'
gcloud container clusters update prod --region="$REGION" \
--add-maintenance-exclusion-name=black-friday \
--add-maintenance-exclusion-start=2026-11-20T00:00:00Z \
--add-maintenance-exclusion-end=2026-12-02T00:00:00Z \
--add-maintenance-exclusion-scope=no_minor_or_node_upgrades
Pre-upgrade checklist:
- [ ] Read GKE release notes for the target minor version
- [ ] Clear deprecated API usage (GKE deprecation insights; kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis)
- [ ] Upgrade Helm charts and operators that pin removed APIs
- [ ] Upgrade a staging cluster on the same channel first
- [ ] Confirm PDBs, replicas >= 2 and readiness probes on critical workloads
- [ ] Get user confirmation for any manual upgrade of production
Manual control-plane upgrade (Standard node pools follow separately; Autopilot nodes follow automatically):
gcloud container clusters upgrade prod --region="$REGION" --master --cluster-version=1.33
GKE blocks automatic minor upgrades when it detects usage of APIs removed in the target version; it does not block manual ones.
Gotchas
kubectl delete namespacedeletes everything inside, including PVC-backed data with a Delete reclaim policy. Always confirm.- Regional clusters spread nodes across three zones; zonal clusters have a single control plane and a maintenance outage for it.
- LoadBalancer Services create external load balancers by default. Use
networking.gke.io/load-balancer-type: "Internal"or Gateway API for internal traffic. - Images from other projects need the node or workload identity to have
roles/artifactregistry.readeron that repository. - Exclusions with
no_upgradesscope are limited to about 30 days and still allow security patches to be forced at end of support.
Verification
kubectl get pods -n shop -o wideshows Running pods across zones;kubectl describeshows no Autopilot resource adjustment warnings.- The workload identity test pod obtains a token and can read only the intended bucket.
gcloud container clusters describeshows the release channel, maintenance window and exclusions you set.- After an upgrade, nodes report the new version and error rates are flat.
Report template
**Cluster:** prod (Autopilot, europe-west1, regular channel, 1.33.x)
**Identity:** shop/api -> roles/storage.objectViewer on gs://shop-assets (direct principal)
**Workloads:** api 3 replicas, requests 250m/512Mi, PDB minAvailable 2, zone spread
**Upgrades:** window Sat-Sun 02:00-06:00 UTC, exclusion black-friday 20 Nov-2 Dec
**Deprecated APIs:** none in last 30 days (deprecation insights) What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Google Kubernetes Engine"
short_description: "Autopilot clusters that upgrade without drama"
default_prompt: "$gcp-gke Create a GKE Autopilot cluster for prod with private nodes and a weekend maintenance window" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.