00 / we run this · ops board

Our production database and cluster, on one screen we built in Rollout

Rollout runs on GKE with a Cloud SQL database. The screen we check them on is an app in our own workspace. It reads Cloud Monitoring and Cloud Logging live, shows only the signals we act on, and runs kubectl on a laptop when the live state of a pod matters. Claude Code built it over Rollout's MCP server.

fig. 1 · read where the data lives what we run

01 / the board

Only what we act on

The database panel shows whether Cloud SQL is up, and its CPU, memory, disk and connection count with a trend for the last hour, six hours or day. The Kubernetes panel shows memory and CPU pressure on each node, container restarts, workload errors and warning events. Each signal has a threshold, and the header shows the worst one. The tabs below open the error log, the warning events, restarts by namespace, and live kubectl.

rollout.dev/apps/cluster
The Ops app: the overall status in the header, a database panel with CPU, memory, disk and connection trends, a Kubernetes panel with node pressure, restarts, workload errors and warning events, and tabs for errors, warning events, restarts and live kubectl
Captured from Rollout's own workspace.

02 / what we dropped

Nothing is stored twice

The first version ran a workflow every 30 minutes that wrote a health record. We retired it. Cloud Monitoring already keeps these series, at a finer grain than a 30-minute record ever would, and alerting belongs in its alert policies. So the app queries the source when it opens and keeps nothing. Records in Rollout are for work that has an owner and a next step, and a CPU sample has neither.

03 / under rules

The app can look, and that's all

The app reaches Google Cloud and the laptop only through the six tools it was granted. The project, the connection, the device and every kubectl command are fixed on those tools. The app fills a PromQL query, a log filter, a time range, a namespace and a pod name, each checked by the tool before it runs. Nothing it can call writes anything.

04 / day one

What it showed the first day

The warning events panel stayed amber, 43 in the hour we captured. Two ExternalSecrets fail to sync every few minutes because the Secret Manager entries they point at don't exist, a real misconfiguration. The rest were probe failures while deploys rolled Rollout's own pods. The database sat at 13% CPU and 66% memory.

05 / how it was built

An assistant did the building, people did the approving

Claude Code wrote the two Google Cloud nodes in the Rollout codebase, then worked in the workspace over MCP: it configured the tools and wrote and published the app. People approved the service account change, pasted the key into the connection, activated the tools, and approved the laptop's kubectl runs and each app upload.

  1. 01 Service account with Logs Viewer, Monitoring Viewer and Kubernetes Engine Cluster Viewer, declared in OpenTofu.
  2. 02 Google Cloud connection allowed only logging.read and monitoring.read. A person pasted the key; it is encrypted and never enters workflow data.
  3. 03 Two nodes added to the Google Cloud integration: Query Cloud Logging and Query Cloud Monitoring. They ship to every workspace.
  4. 04 Six reviewed tools: PromQL now and over a range, log queries on the server; pods, events and pod logs through kubectl on one laptop.
  5. 05 Ops app in React, published from source and granted those six tools.

The build found two platform bugs, both fixed before this page went up. The connection form blamed the name field for a malformed scope, and a tool preset that referenced a caller-filled value reached ordinary nodes as the literal placeholder.

06 / the queries

Copy them

Cloud SQL up · PromQL on Cloud Monitoring

max(cloudsql_googleapis_com:database_up{database_id="PROJECT_ID:INSTANCE"})

Cloud SQL CPU, memory and disk · PromQL, one query each

avg(cloudsql_googleapis_com:database_cpu_utilization{database_id="PROJECT_ID:INSTANCE"})
avg(cloudsql_googleapis_com:database_memory_utilization{database_id="PROJECT_ID:INSTANCE"})
avg(cloudsql_googleapis_com:database_disk_utilization{database_id="PROJECT_ID:INSTANCE"})

Cloud SQL connections to one database · PromQL

sum(cloudsql_googleapis_com:database_postgresql_num_backends{database_id="PROJECT_ID:INSTANCE",database="DATABASE"})

Node pressure · PromQL

max by (node_name) (kubernetes_io:node_cpu_allocatable_utilization)
sum by (node_name) (kubernetes_io:node_memory_allocatable_utilization)

Container restarts over the range · PromQL

sum(increase(kubernetes_io:container_restart_count{monitored_resource="k8s_container"}[1h]))

Workload errors · Cloud Logging

resource.type="k8s_container" AND severity>=ERROR AND resource.labels.namespace_name!="kube-system"

Kubernetes warning events · Cloud Logging

logName="projects/PROJECT_ID/logs/events" AND jsonPayload.type="Warning"

07 / limits

  • Pod readiness and deployment rollout state come from the laptop's kubectl tab. The cluster doesn't export kube-state metrics to Cloud Monitoring, so the server-side panels can't see them.
  • Billing isn't on the board yet. It needs Cloud Billing's export to BigQuery, which this project doesn't have.
  • The Live tab needs the laptop online with the desktop client running. The server-side panels don't.
  • Cloud Logging allows 60 read calls a minute per project by default. The board makes a handful per view.
  • The board is a screen, not a pager. Alerts stay in Cloud Monitoring.

Build the board for your own stack

The logging and monitoring nodes work with any Google Cloud project. Join the waitlist for an alpha workspace.

Prefer to talk it through? Talk to the founder

Node reference: workflow nodes