> ## Documentation Index
> Fetch the complete documentation index at: https://ryvn.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloud datastore metrics

> Enable cloud datastore metrics on AWS, GCP, and Azure. See supported families, default metrics, and how to customize collection.

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](#the-module-contract).

Leave the metric selection unset to collect the [defaults](#default-metrics). Customize it when you need other
metrics within a [supported family](#supported-families). 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](/docs/observability/metrics#grafana-access).

## 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](#the-module-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](#permissions-what-ryvn-reads-in-the-account) for the scope of provider API reads.

> [!NOTE]
>
> 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

| You need | What to do |
| - | - |
| The default metrics | Leave the blueprint's `collection` input unset, or set it to `{}`. |
| More metrics within a supported family | Set that family's `metrics` list. Include any defaults you want to keep: the list replaces them. |
| Fewer metrics | List only the metrics you want, or use `metrics: []` to disable a family. |
| A resource family not listed below | Contact Ryvn for adapter support. Adding a YAML key or a `cloud_metrics` output cannot add a new family. |
| A chart for a custom metric | Query it in Grafana. Custom selections do not add built-in Metrics-tab cards. |

### 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.

| Provider key | Family key | Resource |
| - | - | - |
| `aws` | `rds` | [Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-metrics.html) |
| `aws` | `elasticache` | [Amazon ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/CacheMetrics.Redis.html) |
| `gcp` | `cloudsql` | [Cloud SQL](https://cloud.google.com/monitoring/api/metrics_gcp_c#cloudsql) |
| `gcp` | `memorystore` | [Memorystore for Redis](https://cloud.google.com/monitoring/api/metrics_gcp_p_z#redis) |
| `azure` | `microsoft.dbforpostgresql/flexibleservers` | [Azure Database for PostgreSQL flexible servers](https://learn.microsoft.com/en-us/azure/azure-monitor/reference/supported-metrics/microsoft-dbforpostgresql-flexibleservers-metrics) |
| `azure` | `microsoft.cache/redis` | [Azure Cache for Redis](https://learn.microsoft.com/en-us/azure/azure-monitor/reference/supported-metrics/microsoft-cache-redis-metrics) |
| `azure` | `microsoft.cache/redisenterprise` | [Azure Managed Redis clusters](https://learn.microsoft.com/en-us/azure/azure-monitor/reference/supported-metrics/microsoft-cache-redisenterprise-metrics) |

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](#per-cloud-availability).

**AWS**

#### RDS — `rds`

| Metric | Statistic |
| - | - |
| `CPUUtilization` | `Average` |
| `FreeStorageSpace` | `Average` |
| `ReadIOPS` | `Average` |
| `WriteIOPS` | `Average` |
| `NetworkReceiveThroughput` | `Average` |
| `NetworkTransmitThroughput` | `Average` |

#### ElastiCache — `elasticache`

| Metric | Statistic |
| - | - |
| `EngineCPUUtilization` | `Average` |
| `DatabaseMemoryUsagePercentage` | `Average` |
| `CurrConnections` | `Average` |
| `Evictions` | `Sum` |
| `CacheHits` | `Sum` |
| `CacheMisses` | `Sum` |
| `NetworkBytesIn` | `Sum` |
| `NetworkBytesOut` | `Sum` |
| `ReplicationLag` | `Average` |

**GCP**

GCP entries require `name` containing the full metric type, with no statistic or aggregation field.

#### Cloud SQL — `cloudsql`

| Metric type |
| - |
| `cloudsql.googleapis.com/database/cpu/utilization` |
| `cloudsql.googleapis.com/database/disk/bytes_used` |
| `cloudsql.googleapis.com/database/disk/quota` |
| `cloudsql.googleapis.com/database/network/received_bytes_count` |
| `cloudsql.googleapis.com/database/network/sent_bytes_count` |

#### Memorystore — `memorystore`

| Metric type |
| - |
| `redis.googleapis.com/stats/cpu_utilization_main_thread` |
| `redis.googleapis.com/stats/memory/usage_ratio` |
| `redis.googleapis.com/stats/memory/usage` |
| `redis.googleapis.com/stats/memory/maxmemory` |
| `redis.googleapis.com/clients/connected` |
| `redis.googleapis.com/stats/evicted_keys` |
| `redis.googleapis.com/stats/keyspace_hits` |
| `redis.googleapis.com/stats/keyspace_misses` |
| `redis.googleapis.com/stats/cache_hit_ratio` |
| `redis.googleapis.com/replication/master/slaves/lag` |

**Azure**

#### PostgreSQL — `microsoft.dbforpostgresql/flexibleservers`

| Metric | Aggregation |
| - | - |
| `cpu_percent` | `Average` |
| `storage_used` | `Average` |
| `storage_free` | `Average` |
| `storage_percent` | `Average` |
| `read_iops` | `Average` |
| `write_iops` | `Average` |
| `network_bytes_ingress` | `Total` |
| `network_bytes_egress` | `Total` |
| `active_connections` | `Average` |
| `is_db_alive` | `Average` |

#### Azure Cache for Redis — `microsoft.cache/redis`

| Metric | Aggregation |
| - | - |
| `percentProcessorTime` | `Maximum` |
| `usedmemorypercentage` | `Maximum` |
| `connectedclients` | `Maximum` |
| `evictedkeys` | `Total` |
| `cachehits` | `Total` |
| `cachemisses` | `Total` |
| `cacheRead` | `Maximum` |
| `cacheWrite` | `Maximum` |
| `usedmemory` | `Maximum` |
| `operationsPerSecond` | `Maximum` |

#### Azure Managed Redis — `microsoft.cache/redisenterprise`

| Metric | Aggregation |
| - | - |
| `percentProcessorTime` | `Maximum` |
| `serverLoad` | `Maximum` |
| `usedmemorypercentage` | `Maximum` |
| `connectedclients` | `Maximum` |
| `evictedkeys` | `Average` |
| `expiredkeys` | `Average` |
| `cachehits` | `Average` |
| `cachemisses` | `Average` |
| `cacheRead` | `Maximum` |
| `cacheWrite` | `Maximum` |
| `usedmemory` | `Maximum` |
| `operationsPerSecond` | `Maximum` |

Managed Redis requests the `InstanceId` dimension automatically. Its count metrics use `Average`, unlike legacy
Redis's `Total` counts; do not copy aggregations between the two families.

## 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](/docs/iac/installations/blueprint#inputs), 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.

```yaml theme={null}
kind: BlueprintInstallation
metadata:
  name: cloud-metrics
spec:
  blueprint: ryvn.app/cloud-metrics
  environment: production
  inputs:
    - name: collection
      value:
        aws:
          rds:
            metrics:
              - name: CPUUtilization
                statistics: [Average]
              - name: FreeStorageSpace
                statistics: [Average]
              - name: ReadIOPS
                statistics: [Average]
              - name: WriteIOPS
                statistics: [Average]
              - name: NetworkReceiveThroughput
                statistics: [Average]
              - name: NetworkTransmitThroughput
                statistics: [Average]
              - name: DatabaseConnections
                statistics: [Maximum]
```

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.

> [!WARNING]
>
> 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.

**GCP**

Keep all five Cloud SQL defaults and add disk read operations. Memorystore retains its defaults.

```yaml theme={null}
gcp:
  cloudsql:
    metrics:
      - name: cloudsql.googleapis.com/database/cpu/utilization
      - name: cloudsql.googleapis.com/database/disk/bytes_used
      - name: cloudsql.googleapis.com/database/disk/quota
      - name: cloudsql.googleapis.com/database/network/received_bytes_count
      - name: cloudsql.googleapis.com/database/network/sent_bytes_count
      - name: cloudsql.googleapis.com/database/disk/read_ops_count
```

The added metric is available in Grafana; it does not populate the built-in IOPS card, whose query currently
supports AWS and Azure only.

**Azure**

Collect **only** PostgreSQL CPU and storage percentage. This deliberately drops the family's other defaults;
omitted cache families retain theirs.

```yaml theme={null}
azure:
  microsoft.dbforpostgresql/flexibleservers:
    metrics:
      - name: cpu_percent
        aggregations: [Average]
      - name: storage_percent
        aggregations: [Average]
```

To keep other PostgreSQL metrics, include them with the aggregations listed in [Default metrics](#default-metrics).

### Find metrics outside the defaults

Follow the resource links in [Supported families](#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

| Value | Meaning |
| - | - |
| Input absent, or `{}` | Defaults for every supported family in the environment |
| A family absent from the map | Defaults for that family |
| A family with a nonempty `metrics` list | Exact replacement of that family's defaults |
| A family with `metrics: []` | Collection disabled for that family; declarations stay visible |
| A family present as `{}`, with `metrics: null`, or with a wrong value type | Validation error |
| Unsupported provider, family, field, or statistic/aggregation | Validation error naming the input path |

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](#collection-status) 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:

| Cloud | Tag key |
| - | - |
| AWS | `ryvn.app/cloud-metrics` |
| GCP | `ryvn_app_cloud_metrics` |
| Azure | `ryvn.app-cloud-metrics` |

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:

```hcl theme={null}
variable "ryvn_environment_id" {
  type        = string
  description = "Ryvn environment ID — tags datastore resources for cloud-metrics collection."
}

resource "aws_db_instance" "main" {
  # ... usual configuration ...
  tags = {
    "ryvn.app/cloud-metrics" = var.ryvn_environment_id
  }
}

output "cloud_metrics" {
  value = {
    schema = 1
    targets = {
      this = {
        cloud_resource_id = aws_db_instance.main.arn
      }
    }
  }
}
```

In the blueprint that calls the module, wire the variable to the environment's ID:

```yaml theme={null}
installations:
  - service: your-datastore-module
    config: |
      ryvn_environment_id: "{{ .ryvn.env.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:

| Status | Meaning |
| - | - |
| `unverified` | Opted in and allowlisted. Delivery is reported as unverified rather than collecting/degraded — the hub does not probe the environment's metrics store (it may be unreachable from the hub). Shows `nearing limit` when the environment is close to the resource cap. |
| `not_enabled` | The module emits `cloud_metrics` but the environment hasn't installed the cloud-metrics blueprint — collection is off until it is. |
| `limit_reached` | The environment hit its cloud-metrics cap; this resource's IDs were dropped. |
| `id_conflict` | An earlier installation already claimed one or more of these targets — the detail names them; the installation's other targets still collect. |
| `no_resources` | The output emitted an empty `targets` map. |
| `unsupported_target` | A target's `cloud_resource_id` doesn't resolve to a family the collector serves (unknown id shape, wrong cloud, or no adapter) — the detail names the target; other targets still collect. |
| `invalid_output` | The `cloud_metrics` output is malformed — the detail names the offending target and why. |
| `unsupported_schema` | The output declares a contract version this release doesn't understand. |
| `output_removed` | The module stopped emitting `cloud_metrics` after having been collected. |

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](/docs/observability/metrics#grafana-access) (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):

```promql theme={null}
min by (cloud_resource_id) (aws_rds_free_storage_space_average{environment="production", installation="orders-db"})
```
