Skip to main content
Install the cloud-metrics blueprint to collect managed database and cache metrics from AWS CloudWatch, GCP Cloud Monitoring, or Azure Monitor. Ryvn’s Postgres and Cache modules declare their resources for collection; custom Terraform modules can use the same cloud_metrics output. Leave the metric selection unset to collect the defaults. Customize it when you need other metrics within a supported family. Collected series keep the exporter’s native names, units, and statistics. View supported cards on the installation’s Metrics tab or query the series in Grafana.

How it works

Collection is opt-in for each environment:
  1. Install ryvn.app/cloud-metrics. The blueprint creates a dedicated collector, a read-only cloud identity, and their config. It runs separately from the platform metrics collector. Install and remove it explicitly; datastore installations do not install it automatically.
  2. Declare the resources. Ryvn’s Postgres and Cache modules already emit cloud_metrics and tag resources with the environment ID. Custom Terraform modules must implement the same contract.
  3. Choose the metrics. Use the defaults or set the blueprint’s collection input. Ryvn renders an allowlist of the declared resources and selected metrics into the collector config.
  4. Check delivery. After the identity and collector deploy, allow a few minutes for samples to arrive. Check the installation’s Metrics tab and the environment’s Grafana; an applied config alone does not prove delivery.
The collector’s pods, ServiceAccount, and config belong to the blueprint installation. Blueprint upgrades and removal manage those children through the normal installation lifecycle. See Permissions for the scope of provider API reads.
Every collected series carries environment and installation labels, plus cloud_target (the module-chosen declaration name) and cloud_resource_id with the provider identifier the module declared (the ARN on AWS, the projects/... path on GCP, the ARM id on Azure), so group-shaped resources (a multi-node cache) chart per member.

Choose what to collect

Supported families

A family is a supported cloud service or resource type, not an individual metric. Use the exact key below under your environment’s provider key. Ryvn resolves each target’s family from its cloud_resource_id; the collection input selects metrics for that family across the environment, not for an individual installation. An omitted family uses its defaults, but only declared, admitted targets are collected. The selection does not discover or opt in undeclared resources.

Default metrics

These are the default selections when collection is unset or {}. A provider may not emit every selected metric for every resource, tier, or topology; for example, a cache without replicas may have no replication-lag series. Selected metrics are not a list of guaranteed Metrics-tab cards: see Per-cloud availability.

RDS — rds

ElastiCache — elasticache

Customize collection

Set the map-valued collection input on the environment’s cloud-metrics blueprint installation. Its shape is provider → family → metrics, using only the environment’s own provider. In BlueprintInstallation YAML, it is an entry in spec.inputs.

Keep the defaults and add a metric

This AWS example keeps all six RDS defaults and adds DatabaseConnections with Maximum. Replace production with your environment name. When updating an existing blueprint installation, use its existing installation name.
ElastiCache is omitted, so it retains its defaults. To add a statistic to an existing metric, put both statistics in the same entry, such as statistics: [Average, Maximum]; do not repeat the metric name.
A family’s metrics list replaces its defaults; it never appends. A list containing only DatabaseConnections would stop collecting the default RDS CPU, storage, IOPS, and network metrics.

GCP and Azure input values

The following examples are values of the collection input, not complete installation manifests. Place the chosen map under value in the example above, replacing the aws map.
Keep all five Cloud SQL defaults and add disk read operations. Memorystore retains its defaults.
The added metric is available in Grafana; it does not populate the built-in IOPS card, whose query currently supports AWS and Azure only.

Find metrics outside the defaults

Follow the resource links in Supported families to the provider’s metric reference. Check that the metric is emitted for your resource and supports the statistic or aggregation you select. A new metric within a supported family needs no Ryvn platform release, subject to these limits:
  • AWS: use exact CloudWatch metric names and statistics: Average, Sum, Minimum, Maximum, or SampleCount.
  • GCP: use objects containing only name, set to the full, exact metric type within the family’s domain: cloudsql.googleapis.com/ or redis.googleapis.com/. Scalar strings and additional fields are rejected. Do not use prefixes or wildcards.
  • Azure: use exact Azure Monitor metric names and aggregations: Average, Total, Minimum, Maximum, or Count. Custom dimensions selections are rejected because the exporter cannot bound the number of dimension value combinations. Metrics requiring a custom dimension split, such as ShardMemoryUsedBytes, need exporter support before they can be selected. Managed Redis’s built-in InstanceId split is already configured.

Replacement, reset, and limits

Writing collection replaces the whole map; it does not merge with the previous value. Include every existing override or disabled family you want to retain. For example, omitting an existing elasticache.metrics: [] override re-enables ElastiCache defaults. An update that omits the collection input leaves it unchanged; writing {} explicitly resets it to defaults. Changing the selection updates the generated collector config, not the datastore’s Terraform resources. Pending apply remains set until the collector reconciles the change. Selections allow up to 64 metrics per family and must fit the provider’s estimated collection budget. The estimate counts targets × metrics × statistics/aggregations, weighted by supported dimension sets; GCP counts metric types without statistics. It is not a hard cap on provider API calls or cost: discovered nodes, shards, and dimension values can increase query work. Scrape limits bound retained samples, not the provider work already performed.

The module contract

Two things mark a datastore resource for collection — a tag and an output. The tag key’s spelling depends on the cloud, because GCP and Azure disallow / in tag keys: The tag’s value is always the environment’s Ryvn ID. Only on AWS is the tag the scrape gate — the collector requests metric data only for tagged resources. GCP and Azure gate by enumeration instead: the rendered config lists the declared resource ids verbatim (GCP extra_filters; an Azure Resource Graph id in~ filter), and the tag is a bookkeeping convention there. In the module:
In the blueprint that calls the module, wire the variable to the environment’s ID:
For resources applied outside a Ryvn module, ryvn describe environment prints the environment’s ID for manual tagging.
  • schema is 1 — the contract version.
  • targets maps a stable, module-chosen name to each provider object. The name travels onto the collected series as a cloud_target label — keep it short and stable (it must match [a-zA-Z0-9][a-zA-Z0-9_-]{0,62}).
  • cloud_resource_id is the provider-native identifier — the full ARN on AWS, the Terraform resource path on GCP (projects/<project>/instances/<name> for Cloud SQL, projects/<project>/locations/<location>/instances/<name> for Memorystore), and the full ARM id on Azure (/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.DBforPostgreSQL/flexibleServers/<name> or Microsoft.Cache/redis). The identifier alone carries the service, resource kind, region, and cloud account; Ryvn resolves the metric family and dimensions from it, so the module never spells a Ryvn-side type or region. For cluster-shaped resources declare every member and the group as separate named targets (for ElastiCache, each cluster ARN plus the replication-group ARN).
  • selection is optional provider-specific context for targets whose identifier is not self-describing (e.g. a project-level quota object); datastore identifiers never need it.
Tag every member resource of a group: on AWS, metric-data requests are issued only for resources carrying the tag with exactly this environment’s ID — a value with an extra prefix or suffix never matches — so an untagged replica is never scraped. On GCP and Azure an undeclared resource is never scraped even if tagged, because the metrics queries enumerate the declared ids.

Permissions: what Ryvn reads in the account

Each cloud grants the dedicated collector a read-only metrics identity through the blueprint’s <provider>-identity child, so the exact grant shows up in that installation’s plan and removing the blueprint removes the binding with it — the platform collector’s identity is never widened for cloud metrics:
  • AWS — an IAM role granting cloudwatch:GetMetricData, cloudwatch:GetMetricStatistics, cloudwatch:ListMetrics, and tag:GetResources, provisioned by the aws-cloud-metrics-identity child. To audit actual reads, enable CloudTrail data-event logging for CloudWatch in the account: GetMetricData and GetMetricStatistics are data events, so they do not appear in a management-events-only trail.
  • GCP — the collector’s Kubernetes ServiceAccount is itself the Workload Identity principal; roles/monitoring.viewer is granted on exactly the projects the environment’s targets point into (the union the hub renders as the blueprint’s monitoredIds input).
  • Azure — a user-assigned managed identity with Monitoring Reader on the subscriptions the environment’s targets point into (the same monitoredIds seam), bound to the collector’s ServiceAccount by a federated credential.
Be precise about what those controls bound — none of them is an IAM boundary:
  • The tag (AWS) confines metric-data requests: GetMetricData is issued only for resources the tag admitted. It does not confine enumeration — ListMetrics and tag:GetResources still run across the account in the granted regions, because discovery works by listing candidates and matching the tag. On GCP and Azure the metrics queries themselves enumerate the declared ids (per-project extra_filters; a Resource Graph id in~ filter), so undeclared ids are never requested at all.
  • The rendered allowlist and its 50-target cap bound which resources’ telemetry is forwarded to the metrics store — they are scoping rules on emitted series, not on provider API reads.
  • Forwarding is default-deny on resource identity: only samples carrying a current admitted resource claim reach the store — exporter self-metrics and anything unrelated never join the cloud-data stream.
The identity boundary is each grant’s own scope, nothing more: the AWS role can call its granted APIs on whatever those APIs expose in the account, the GCP and Azure grants are already scoped to the declared projects and subscriptions (and their queries enumerate declared ids inside that scope), and the tag/enumeration/allowlist/cap chain only decides what is requested and retained.

Per-cloud availability

Each cloud’s metrics API covers a slightly different surface; a card for a metric a cloud doesn’t emit shows unavailable rather than zero:
  • GCP — Cloud SQL does publish per-disk read/write ops DELTA metrics (database/disk/read_ops_count / write_ops_count), but they’re left out of the recommended defaults, and the built-in IOPS card’s query only unions the AWS and Azure arms today — selecting the GCP ops metrics makes the stackdriver_ series available to query but the card itself stays empty. Memorystore CPU reports the main thread’s per-window utilization rather than an all-core average (that is the thread Redis bottlenecks on), and Memorystore has no network-throughput metrics, so cache network cards show unavailable.
  • Azure — two cache families are supported side by side: legacy Azure Cache for Redis (microsoft.cache/redis) and Azure Managed Redis (microsoft.cache/redisenterprise, keyed by the ARM cluster resource id). Azure Cache doesn’t publish a replication-lag metric — Managed Redis exposes geoReplicationHealthy (0/1 health, not a lag), so the replication-lag card shows unavailable on Azure rather than a misleading lag series. Managed Redis shards report under the per-shard InstanceId dimension, so cache cards sum across shards for a cluster total; cacheRead/cacheWrite remain inbound/outbound respectively (bytes read from / written to the cache). Managed Redis counts (cachehits, cachemisses, evictedkeys, expiredkeys) arrive already per-second — the collector reads them as Average series and the cards take them as-is, unlike legacy Total counts which the cards divide by the scrape window. The InstanceId split is requested with a wildcard (InstanceId eq '*'): the exporter may expand it into more query work than the declared collection estimate — an accepted residual of the stock Azure Monitor exporter, documented rather than capped.
  • GCP and Azure — free-space panels need no extra plumbing: Cloud SQL composes its own bytes_used/disk/quota pair and Azure emits storage_free directly.

Collection status

Status is a stored field — cloudMetrics on the installation’s metrics response (MetricsResponse) — so the web UI, the CLI, and direct API callers all read the same value. The Metrics tab shows a status line for installations that emit cloud_metrics, and ryvn get cloud-metrics [NAME] -e ENVIRONMENT prints the same status for one installation or a whole environment: Alongside the status code, the same surface carries two pieces of collection state:
  • Pending apply — when the rendered collector config differs from what the blueprint install currently carries, the status shows pending apply (CLI) and the Metrics tab notes it. A collection change, a new declaration, or a resource removal all render a new config; the flag clears once the collector’s reconcile applies it. It is informational, not an error — the collector keeps serving the previously applied selection in the meantime.
  • Family selection — each resolved family reports enabled or disabled. Disabled means the family is present in declarations but the collection selection turned it off (metrics: []); the CLI shows the disabled family names next to the status.
Each environment collects up to 50 admitted targets (member and group targets each count; disabled families consume none). The cap bounds the allowlist itself — a target that overflows it is dropped alone, reported limit_reached in the detail, and the installation’s other targets still collect. Admission is deterministic for a given input but membership is not stable across renders: a new emitter can change which later targets fit under the cap. Two more rules shape admission: a cloud object associates with one installation — if two installations declare the same cloud_resource_id, the earlier declaration wins and the other installation’s id_conflict detail names the loser targets — and within one output, two names resolving to the same cloud object admit only the first declaration’s name (the alias is recorded as a duplicate, never double-scraped).

Where the metrics live

Collected series are written to the environment’s own metrics store alongside its other observability data — they are not replicated into a hub-level store. Two consequences:
  • You query them through that environment’s Grafana (or any client pointed at its store); there is no cross-environment rollup of cloud metrics.
  • An environment without the observability stack produces no queryable series — the collector has nowhere to write. Because the hub never queries the destination, installation status stays unverified whether or not series actually land; check the environment’s Grafana to confirm delivery.

Dashboards and queries

Series keep the provider exporter’s native names — aws_rds_cpuutilization_average on AWS, stackdriver_cloudsql_database_cloudsql_googleapis_com_database_cpu_utilization on GCP, azure_microsoft_dbforpostgresql_flexibleservers_cpu_percent_average_percent on Azure — with native units, statistic suffixes, and counter semantics preserved. Ryvn adds only attribution labels (environment, installation, cloud_target, cloud_resource_id); it never renames or re-units provider metrics, and never dual-publishes old normalized aliases. The built-in Metrics-tab cards query the native metric names for each family’s recommended selection. Because collection can replace or disable that selection:
  • A card whose prerequisite metric isn’t in your selection shows empty/unavailable rather than silently widening the collector or fabricating zeroes.
  • Custom metrics you add don’t automatically acquire cards — Grafana and direct queries are the advanced path, and they work for any native name the collector forwarded.
To chart free storage for a database named orders-db (native CloudWatch name, native bytes unit):