# GKE Node Pool Not Using COS Image

> Container-Optimized OS ships a read-only root filesystem, automatic updates, and a minimal package surface. ZopNight rule RC-1225 flags GKE node pools whose live image_type is anything other than COS or COS_CONTAINERD: Ubuntu and Windows images fire, absent metadata abstains. Remediation updates the pool to COS_CONTAINERD via a surge upgrade.

Source: https://zop.dev/integrations/gcp/recommendations/gke-node-pool-not-using-cos-image
Updated: 2026-08-19

---

## Moving a pool to COS is an image update, not a rebuild

Unlike GKE's recreate-only settings, the node image is mutable per pool: change `image_type` and GKE rolls replacement nodes through a surge upgrade, draining the old ones as their substitutes come up. The change command is one line:

```bash
gcloud container node-pools update my-pool --cluster my-cluster \
  --location us-central1 --image-type COS_CONTAINERD
```

The real work is the verification beforehand. Anything that assumed Ubuntu underneath will surface during the roll: workloads installing packages with apt on the host, DaemonSets loading out-of-tree kernel modules, or tooling that expects a writable root filesystem. Windows pools are a different case entirely; workloads built for `WINDOWS_LTSC` images cannot move to COS, and for them the finding documents a constraint rather than an action.

## What COS buys at the node layer

Container-Optimized OS is Google's purpose-built node image: a minimal, Chromium OS-derived system whose root filesystem mounts read-only, whose package surface is a fraction of a general-purpose distro's, and whose updates arrive automatically tied to the GKE release flow. Fewer installed components mean fewer CVEs applying to the node at all (the vulnerability-scanning delta between a COS pool and an Ubuntu pool is visible in any scanner report), and the read-only root turns a class of node-level persistence techniques into a non-event. Ubuntu node images remain fully supported by GKE; the rule encodes a hardening preference, not a deprecation.

## The rule's read, and when it stays quiet

Rule RC-1225 evaluates node pools individually, the only rule in the GKE compliance set scoped to `gke-nodepool` rather than the cluster. It reads the live `image_type` the discoverer captures from the pool's config, fires on any value that is not `COS` or `COS_CONTAINERD`, and abstains when the key is absent. An unobserved pool is never flagged, and there is no tags fallback. The detection proves the image family from provider config; what it cannot see is whether workloads on the pool actually depend on the non-COS image.

## Confirm a pool's image

```bash
gcloud container node-pools describe my-pool --cluster my-cluster \
  --location us-central1 --format='value(config.imageType)'
```
