Scale GKE Node Pools to Zero Overnight with gcloud
Resize a GKE Standard node pool to zero in the evening and back in the morning, without the cluster autoscaler undoing it.
GKE has no stop button for a cluster. What you can do on a Standard cluster is shrink a node pool to zero nodes after hours, which deletes the node VMs and ends their Compute Engine charge, then grow it again before people arrive. This guide covers the two gcloud commands that matter, the autoscaler setting that silently reverses them, and what still bills while the pool is empty.
Autopilot clusters are out of scope: Google manages the nodes, and Autopilot workloads are priced on the compute resources their Pods request, so the lever there is scaling Deployments down, not resizing pools.
What you need first
- A GKE Standard cluster with at least two node pools: one that stays up for system Pods, and one or more you want to empty.
- gcloud authenticated as a principal holding
container.clusters.updateon the project (resize and autoscaling changes are cluster updates). kubectlpointed at the cluster, to watch Pods move.
1. Record each pool before you touch it
gcloud container node-pools list --cluster dev-cluster --location europe-west1
gcloud container node-pools describe apps-pool \ --cluster dev-cluster --location europe-west1 \ --format="yaml(autoscaling)"
kubectl get nodes -l cloud.google.com/gke-nodepool=apps-poolWrite down the node count and, if autoscaling is on, its minNodeCount and maxNodeCount. You need these numbers to restore the pool in the morning. In a pool that spans several zones, sizes are per zone: --num-nodes=2 on a three-zone pool means six nodes.
2. Leave one pool running
The cluster autoscaler on Standard clusters never scales a whole cluster to zero, because one or more nodes must be available to run system Pods. Keep a small pool for those and only empty the pools that carry your application workloads.
3. Evening: turn off autoscaling, then resize to zero
If autoscaling stays on, the Pods you evict become Pending and the autoscaler adds nodes back to place them. Disable it on the pool first:
gcloud container clusters update dev-cluster \ --no-enable-autoscaling \ --node-pool apps-pool \ --location europe-west1
gcloud container clusters resize dev-cluster \ --node-pool apps-pool \ --num-nodes 0 \ --location europe-west1The resize drains the nodes and gracefully terminates their Pods. Pods owned by a Deployment or StatefulSet go Pending until capacity returns. Bare Pods with no controller are not restarted, so move anything you care about under a controller first. Resize picks which nodes to remove at random.
4. Morning: resize back and restore autoscaling
gcloud container clusters resize dev-cluster \ --node-pool apps-pool \ --num-nodes 2 \ --location europe-west1
gcloud container clusters update dev-cluster \ --enable-autoscaling \ --node-pool apps-pool \ --min-nodes 1 --max-nodes 4 \ --location europe-west1Use the numbers you recorded in step 1. --min-nodes and --max-nodes are also per zone; --total-min-nodes and --total-max-nodes set whole-pool limits instead and cannot be mixed with the per-zone flags.
Check that it worked
kubectl get nodes -l cloud.google.com/gke-nodepool=apps-poolkubectl get pods -A --field-selector=status.phase=PendingAt night the first command returns no nodes and the Pending list shows the workloads waiting for the morning. After the morning run, nodes report Ready and the Pending list empties.
What keeps billing while the pool is empty
- Cluster management fee. GKE charges a flat $0.10 per cluster per hour whatever the node count. The free tier gives each billing account $74.40 a month in credit, enough for one zonal Standard or Autopilot cluster; it does not cover regional clusters.
- Persistent disks behind PersistentVolumes. A resize does not delete PVCs, and Persistent Disk is billed on provisioned space until you delete the disk.
- The pool you kept for system Pods, plus any load balancers and reserved addresses the cluster owns.
Doing it with ZopNight
ZopNight discovers GKE clusters and node pools and can put a node pool on a schedule with start and stop cron expressions in an IANA timezone, the same schedules it uses for VMs. The account must be connected read + write for the schedule to execute; a read-only connection still shows the pools and their recommendations. The GKE node pool schedule page covers the resource-specific details, and namespace vs cluster scheduling explains when emptying a namespace beats emptying a pool.