Cloud metrics for RDS and Aurora
An RDS or Aurora database’s engine cannot see the host it runs on: its CPU, its free memory and storage, its disk latency, its burst credits. Cloud metrics fill that in. The agent on the database’s active collector reads them from Amazon CloudWatch and the RDS API, with the collector’s own EC2 instance role, and sends them to WatchTower15 beside the engine’s own figures. For Amazon RDS for MySQL, RDS for MariaDB and Aurora MySQL. Read Amazon RDS and Aurora first for the endpoint, the grant script and the credentials.
Where you see them
Section titled “Where you see them”| Where | What |
|---|---|
| Overview | The Capacity view of the Telemetry card, on the page’s time range: below. Only on RDS and Aurora, never on an RDS Proxy endpoint or a database outside AWS. The Monitoring coverage gauge counts the layer as 1 while Collected, half with only rds:DescribeDBClusters denied or while Throttled, and 0 in any other state; an RDS Proxy endpoint leaves it out of the count. See The Overview. |
| Info tab | The Cloud metrics card: the state, each collector’s check (Server, Role, Cloud metrics, Checked), Test again and The read policy and its cost. Under it, the Instance (from the RDS API) card: its identifier, cluster and role, class, storage, allocated size and autoscaling limit, Multi-AZ, engine version, Performance Insights, the metrics requested and their cost, and when the facts were reported (before the first read, No RDS facts yet); and the Collection card: the state’s tag with a few words, such as Permission denied · The role was refused cloudwatch:GetMetricData, Read by (the active collector, with its own instance role), RDS API (every hour, and on Test again) and CloudWatch (every 5 minutes, 1-minute points). The Layers available card’s L7 Cloud metrics (CloudWatch) row: the state. |
| Alarms tab | The three RDS alarms. |
| Add database | Step 4 has an optional Cloud metrics section with the policy and its cost; step 5 shows each candidate’s Cloud metrics: check beside its connection check. |
| Storage & growth | On RDS, the storage ceiling AWS reports: days to full without entering anything. |
The Capacity view
Section titled “The Capacity view”On the database’s Overview, the Telemetry card’s Performance | Capacity switch
shows the CloudWatch charts on Capacity. The card’s one time range picker sets the range for
both views. A link to the database’s page ending in /telemetry opens the Overview on
Capacity. The instance’s facts and how they are collected are cards on the Info tab
(above).
While the data is not collected, the view opens with the state’s message: what is wrong and what to do, such as No CloudWatch metrics: the collector’s role was refused cloudwatch:GetMetricData. or Waiting for the collector’s first match. Every state, its message and its fix: When something is wrong. Below the message:
| Part | Shows |
|---|---|
| Read policy | Only while the collector’s role was refused a read (other than rds:DescribeDBClusters alone), or its instance has no role: the read policy to attach, with its cost. |
| Host metrics (CloudWatch) | The charts, once the RDS facts are known, under the late line with the exceptions that apply. On Aurora, a note under them says when replica lag is not requested: on an instance that is not a reader, or while rds:DescribeDBClusters is denied. |
The state tags: Collected, Permission denied, Not found, IMDS unavailable, Throttled, Unreachable, AWS error, RDS Proxy, Agent too old and Waiting.
What is read
Section titled “What is read”From CloudWatch, every 5 minutes
Section titled “From CloudWatch, every 5 minutes”One GetMetricData call every 5 minutes, for 1-minute points (the Average, and the Maximum
for replica lag), with only the metrics that apply to the instance:
CloudWatch metric (AWS/RDS) |
Chart | Read for |
|---|---|---|
CPUUtilization |
CPU (%) | Every database |
CPUCreditBalance |
CPU credit balance | Burstable classes (db.t…) |
FreeableMemory, SwapUsage |
Freeable memory and swap (GiB) | Every database; no swap on Aurora db.r3, db.r4 and db.r7g, which AWS publishes none for |
FreeStorageSpace, beside the allocated size from the RDS API |
Free storage against allocated (GiB) | RDS |
FreeLocalStorage |
Local storage free (GiB) | Aurora |
VolumeBytesUsed |
Cluster volume used (GiB) | Aurora |
ReadIOPS, WriteIOPS |
IOPS | Every database |
ReadLatency, WriteLatency |
Disk latency (ms) | Every database |
ReadThroughput, WriteThroughput |
Disk throughput (MB/s) | Every database |
DiskQueueDepth |
Disk queue depth | Every database |
BurstBalance |
Burst and EBS balances (%) | RDS on gp2 storage |
EBSIOBalance%, EBSByteBalance% |
Burst and EBS balances (%) | RDS classes up to 2xlarge |
NetworkReceiveThroughput, NetworkTransmitThroughput |
Network (MB/s) | Every database |
DatabaseConnections |
Connections | Every database |
ReplicaLag |
Replica lag (s) | An RDS read replica, or a reader of an RDS Multi-AZ DB cluster |
AuroraReplicaLag |
Replica lag (s) | An Aurora reader |
BinLogDiskUsage |
Binary logs on disk (GiB) | RDS |
DBLoad |
Database load (sessions) | While Performance Insights (Database Insights) is on |
That is 14 to 21 metrics per database. A metric AWS does not publish for an instance returns
no points: its chart stays empty, and nothing is wrong. Database load is CloudWatch’s own DBLoad: WatchTower15 never calls the
Performance Insights API, and never turns Performance Insights on for you.
From the RDS API, every hour
Section titled “From the RDS API, every hour”One DescribeDBInstances call, filtered to the one instance, every hour, at start and on
Test again: the facts in the Info tab’s Instance (from the RDS API) card
(Where you see them), which metrics apply, and on
RDS the allocated size and the storage autoscaling limit. A cluster member’s role (writer or
reader) comes from one DescribeDBClusters call filtered to its cluster. When storage
autoscaling grows the volume, the next poll sees more free space than the allocated size it
knows, and reads the new size at once (at most once every 15 minutes), so the free-storage share
follows within one poll. The RDS calls are free.
Turning it on: the read policy
Section titled “Turning it on: the read policy”Attach this policy to the IAM role your collector servers use (their instance profile). It grants
eight read actions, in two statements, and nothing else: six for CloudWatch metrics and RDS facts
(WT15CloudMetricsRead), and two that read the instance’s
slow and error logs (WT15DatabaseLogsRead). The Cloud
metrics card and step 4 of the setup show it with Copy, and so does the Logs card beside
a log whose reads were refused.
CloudFormation:
# CloudFormation: attach to the role your collectors' instance profile uses.WT15CloudMetricsRead: Type: AWS::IAM::RolePolicy Properties: RoleName: <your collector role> PolicyName: wt15-cloud-metrics-read PolicyDocument: Version: "2012-10-17" Statement: - Sid: WT15CloudMetricsRead Effect: Allow Action: - cloudwatch:GetMetricData - rds:DescribeDBInstances - rds:DescribeDBClusters - rds:DescribeDBProxies - rds:DescribeDBProxyEndpoints - rds:DescribeDBProxyTargets Resource: "*" - Sid: WT15DatabaseLogsRead Effect: Allow Action: - rds:DescribeDBLogFiles - rds:DownloadDBLogFilePortion Resource: "arn:aws:rds:*:*:db:*"Terraform:
# Terraform: attach to the role your collectors' instance profile uses.resource "aws_iam_role_policy" "wt15_cloud_metrics_read" { name = "wt15-cloud-metrics-read" role = "<your collector role>" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "WT15CloudMetricsRead" Effect = "Allow" Action = [ "cloudwatch:GetMetricData", "rds:DescribeDBInstances", "rds:DescribeDBClusters", "rds:DescribeDBProxies", "rds:DescribeDBProxyEndpoints", "rds:DescribeDBProxyTargets", ] Resource = "*" }, { Sid = "WT15DatabaseLogsRead" Effect = "Allow" Action = [ "rds:DescribeDBLogFiles", "rds:DownloadDBLogFilePortion", ] Resource = "arn:aws:rds:*:*:db:*" }, ] })}- Every collector in the pool should carry it: a standby that takes over without it stops the cloud metrics, and the Cloud metrics card and step 5 warn about that standby. A collector without the policy can still collect the database’s engine metrics.
rds:DescribeDBClustersfinds a cluster writer endpoint’s current writer and a cluster member’s role. The threeDescribeDBProxy…actions are used only for an RDS Proxy endpoint.- The second statement,
WT15DatabaseLogsRead, is used only by the slow and error logs: its two actions act on DB instances only. A role that carries only the first statement reads cloud metrics, and its databases’ Logs card reads Permission denied: attach the full policy to the role again. Its name,wt15-cloud-metrics-read, is the same. - Narrowing it.
cloudwatch:GetMetricDatatakes no resource, so it stays on*. You may narrow therds:actions to your own ARNs: cover the instances, clusters and proxies you monitor, and the first label of each database’s endpoint (a cluster’s, custom endpoint’s or proxy’s name), which the collectors’ checks ask for. Otherwise the check reports that denial. - There is nothing to set on the database, and no per-database switch: the policy is read by every RDS and Aurora database these collectors collect.
What it costs
Section titled “What it costs”AWS bills CloudWatch’s GetMetricData to your account: $0.01 per 1,000 metrics
requested. Polled every 5 minutes, one metric costs about $0.0876 a month, so a database
costs about $1.23 to $1.84 a month (14 to 21 metrics). The policy block states the
database’s own figure once it is known:
About $1.58 a month for this database: 18 CloudWatch metrics, every 5 minutes, at AWS’s $0.01 per 1,000 metrics requested, billed by AWS to your account. The RDS calls are free.
- Only the active collector polls, so a pool never multiplies the cost.
- A standby’s hourly check reads one metric for one minute: about $0.0073 a month. A probe-only candidate’s check calls the RDS API only, which is free.
- While telemetry is off for the collector’s host (its profile, or
WT15_DISABLE_TELEMETRY), it makes noGetMetricDatacall: it costs nothing, and the CloudWatch charts stop where the points stop. The hourly RDS reads and the checks, which are free, carry on. - The slow and error log reads are RDS calls, which are free.
- WatchTower15 does not bill cloud metrics: a database is one billable resource, whatever it reads.
What a collector needs
Section titled “What a collector needs”| Need | Why | If it is missing |
|---|---|---|
| An instance role: an instance profile attached to the EC2 instance, its role carrying the policy | The agent signs each call with the role’s credentials, read from the instance metadata | IMDS unavailable — No instance role on the collector |
| The instance metadata service (IMDSv2), reachable from the agent | Where the role’s credentials come from. In a container, set the instance’s metadata hop limit to 2: aws ec2 modify-instance-metadata-options --instance-id <id> --http-put-response-hop-limit 2 |
IMDS unavailable — No answer from IMDS |
A route to rds.<region>.amazonaws.com and monitoring.<region>.amazonaws.com on 443 |
The RDS API and CloudWatch, in the database’s region | Unreachable |
| A clock within 5 minutes of AWS’s | AWS refuses a signed request whose time is further off | AWS error — The collector’s clock is off |
| The database’s own AWS account | A collector reads only its own account and region | Not found — Not in this AWS account and region |
The route. Any of these works:
- a NAT gateway (or a public subnet with a public address);
HTTPS_PROXYin the agent’s environment: the collector’s calls to AWS honour the standardHTTPS_PROXYandNO_PROXYvariables.WT15_PROXY, the agent’s own proxy for WatchTower15 traffic, is not used for them;- VPC interface endpoints for the RDS API (
com.amazonaws.<region>.rds) and CloudWatch (com.amazonaws.<region>.monitoring), with private DNS on.
No inbound rule is needed anywhere: the collectors call out, as they do for everything else.
Same account only. A collector reads the RDS instances of its own AWS account and region. For a database in another account, add a collector server in that account.
How late the points are
Section titled “How late the points are”CloudWatch publishes each minute 2–3 minutes after it ends, and the collector reads every 5 minutes, so the Host metrics (CloudWatch) panel says:
CloudWatch data arrives late: the newest point can be up to about 8 minutes old (CloudWatch’s 2–3 minutes, then the 5-minute poll); the CPU credit balance up to about 13 minutes (AWS publishes it every 5 minutes); the cluster volume up to about an hour (AWS collects it at intervals).
The line names only the exceptions that apply to the instance. Every point keeps CloudWatch’s own timestamp, which a collector’s clock never shifts, so the charts line up with the AWS console. At every time range each chart is a continuous line, the 5-minute credit balance and the hourly cluster volume included.
- While WatchTower15 cannot be reached, the points wait on the collector with the rest of its telemetry, and arrive once it can.
- While AWS cannot be reached, nothing is read; the next poll that gets an answer fetches up to the last 15 minutes it missed. Older points are not read.
- After a restart or a change of active collector, the first poll starts 5 minutes back.
Which endpoints are matched
Section titled “Which endpoints are matched”The host the database is set up with decides which instance’s metrics are read:
| Host | What is read |
|---|---|
An instance endpoint (<instance>.<id>.<region>.rds.amazonaws.com) |
That instance. |
An Aurora cluster writer endpoint (.cluster-) |
The instance that answers the collector, so the metrics follow a failover at once. Before the first answer, and while the database does not answer, the cluster’s writer from the RDS API. |
An Aurora reader (.cluster-ro-) or custom (.cluster-custom-) endpoint |
The instance that answers the collector, as the engine’s own figures do; the Capacity view waits for the first answer. Each connection can reach another instance, so prefer an instance endpoint (Pooled endpoints). |
| An RDS Multi-AZ DB cluster’s writer endpoint | The cluster’s writer. |
| An RDS Multi-AZ DB cluster’s reader or custom endpoint | Nothing: RDS does not say which reader answers. Not found: use an instance endpoint or the writer endpoint. |
| Your own DNS name | Its CNAME chain, followed one name at a time (up to 8), to the first RDS endpoint name, matched as above. A name with no CNAME, or a chain that reaches no RDS name, is Not found: use the RDS endpoint name. |
An Aurora Global Database writer endpoint (.global-<id>.global.rds.amazonaws.com) |
Followed to the primary cluster’s writer endpoint. |
An RDS Proxy endpoint (.proxy-, a custom proxy endpoint, or your own name for one) |
No metrics: the instances behind it, below. |
For a cluster endpoint, the instance must also carry the endpoint’s account-and-region part of its name, so an instance with the same name in the collector’s own account is never taken for yours.
RDS Proxy endpoints
Section titled “RDS Proxy endpoints”A proxy spreads connections over several instances, so it has no CloudWatch metrics of its own, and the database’s Overview has no Capacity view. Instead, every hour, the collector lists the instances behind it, and the database’s page shows them under Behind RDS Proxy <name>, in a banner under the tab bar (inside the This endpoint mixes several servers banner while that one shows). The banner opens This endpoint is an RDS Proxy, or This host leads to an RDS Proxy for a name of your own, and ends …so it has no CloudWatch metrics of its own. Each instance behind the proxy is listed with:
| Column | Shows |
|---|---|
| Target | The instance’s identifier, and its endpoint and port |
| Role | Writer, Reader or Unknown |
| Health | AWS’s target health, such as Available, or Unknown |
Add as a database (database.edit) opens the setup prefilled with that instance’s endpoint and
port (3306 when AWS gives none) and the proxy database’s own provider, so a proxy in front of
Aurora adds Aurora instances. Nothing is created until you complete the setup. Each instance you
add gets its own counters, alarms and cloud metrics. Only instance targets are listed: an Aurora
proxy’s tracked cluster is not.
The collectors’ checks
Section titled “The collectors’ checks”The Cloud metrics card on the Info tab, and step 5 of the setup, show what each collector’s role can do, so you know what happens if it takes over:
| Collector | Checks | Passes as |
|---|---|---|
| Active | Its own reads (the database’s state) | Reads RDS and CloudWatch |
| Standby | One DescribeDBInstances and one minute of CPUUtilization, every hour and on Test again |
Reads RDS and CloudWatch |
| Probe only | One DescribeDBInstances at each probe |
Reads RDS (CloudWatch is checked once it is the standby) |
A standby or a candidate does not know the instance yet, so its check asks for the endpoint’s
first label. AWS checks the permission before it looks for the instance, so an answer that no
such instance exists still passes: the check proves the role, not the match. Test again
(database.edit) asks every candidate to probe again within about 15–35 seconds, with its cloud
check, and makes the active collector read the RDS API and retry a refused call at once.
What appears in your CloudTrail
Section titled “What appears in your CloudTrail”| Who | Calls |
|---|---|
| The active collector | DescribeDBInstances every hour (and DescribeDBClusters for a cluster member or a cluster writer endpoint); GetMetricData every 5 minutes; for an RDS Proxy, DescribeDBProxies, DescribeDBProxyEndpoints and DescribeDBProxyTargets every hour |
| A standby | DescribeDBInstances and GetMetricData once an hour |
| A probe-only candidate | DescribeDBInstances at each probe |
| The active collector, for the slow and error logs | DescribeDBLogFiles twice a minute (slowquery/ and error/), and DownloadDBLogFilePortion only while a log’s file changes, at most 5 a minute |
- A refused call is retried every hour and on Test again, never every 5 minutes, so a missing policy does not fill your trail with errors. So is any other error AWS answers with (a clock skew, refused credentials).
- No answer at all — a timeout, no route, a DNS or TLS failure, or an AWS server error — is retried at the next 5-minute poll: nothing reached AWS’s permission check.
- The checks ask for an instance named
wt15-role-checkwhen the endpoint’s first label is not a valid RDS identifier (a name of your own that starts with a digit, for example). - Only these eight read actions: never a write, never a Performance Insights call.
The three RDS alarms
Section titled “The three RDS alarms”Every database lists three more default alarms, tuned like any other on its Alarms tab:
| Alarm | Fires when | Default |
|---|---|---|
| RDS free storage low | The free share of the RDS instance’s allocated storage | warning 20 %, critical 10 % |
| RDS CPU high | The CPU of the RDS or Aurora host, averaged over 10 minutes | warning 80 %, critical 95 % |
| RDS storage burst balance low | The gp2 volume’s I/O burst credits left |
warning 20 %, critical 10 % |
- Listed on every database, evaluated only where the series exist: never on a self-managed
database, nor where the collectors’ role does not carry the policy. RDS free storage low is
never evaluated on Aurora, which has no allocated storage, and RDS storage burst balance low
only on
gp2storage, the only type with burst credits. Their tiles then read No data, with the reason. - With storage autoscaling on, RDS grows the volume only after free space has stayed at or below 10 % for 5 minutes, so RDS free storage low can open a warning, and a short critical incident, minutes before RDS acts.
- Their newest point can be up to about 8 minutes old, so each waits for 10–15 minutes of points before it fires.
- They sit under Host (CPU) and Storage on the Alarms tab, and read as percentages.
Storage and days to full
Section titled “Storage and days to full”On RDS for MySQL and RDS for MariaDB, the storage AWS reports — the storage autoscaling limit, else the allocated size — becomes the database’s storage ceiling, and its free space is AWS’s own (plus the autoscaling headroom). Days to full then needs nothing entered by hand. See Days to full and the ceiling.
When something is wrong
Section titled “When something is wrong”The Overview’s Capacity view shows the state’s full message first, with what to do; the charts it last had stay below it, and the Info tab keeps the last facts. The Info tab’s Collection and Cloud metrics cards show the same state as a tag.
| State | Means | Fix |
|---|---|---|
| Collected | CloudWatch metrics and RDS facts are read. | — |
| Waiting | No match reported yet: the active collector matches the database when it starts and every hour, and a reader or custom endpoint waits for its first answer. | None: it comes within minutes. If it stays, check that the collector is the active one and its agent is running. |
| Agent too old | The active collector’s agent predates cloud metrics. | The agent updates itself within minutes of a release; check that it can. |
| Permission denied — the role was refused <action> | The collector’s role does not carry the policy, or not that action. Nothing is polled, and nothing is billed. | Attach the read policy to the role your collectors use, then Test again. Retried every hour. |
| Permission denied — rds:DescribeDBClusters denied; the other metrics are still read | The collector cannot read the cluster: the instance’s role and its replica lag are unknown. | Attach the full policy. Retried every hour. |
| Not found — Not in this AWS account and region | No instance with this endpoint in the collector’s account and region. | Add a collector server in the database’s own account. |
| Not found — The host is not an RDS endpoint name | Your own DNS name leads to no RDS endpoint name. | Use the RDS endpoint name as the host (the RDS console → the instance → Connectivity). |
| Not found — A Multi-AZ DB cluster reader endpoint | An RDS Multi-AZ DB cluster’s reader or custom endpoint. | Use an instance endpoint, or the cluster writer endpoint. |
| IMDS unavailable — No instance role on the collector | The instance metadata answers, but no role is attached. | Attach an instance profile with a role (the EC2 console → the instance → Actions → Security → Modify IAM role), then the policy to that role. |
| IMDS unavailable — No answer from IMDS | The agent cannot reach the instance metadata. | In a container, set the hop limit to 2; turn IMDSv2 on; run the collector on EC2. |
| Throttled | AWS throttled a request: the collector waits twice as long before the next, up to an hour. | None; the newest points can be more than 8 minutes old until it recovers. |
Unreachable — No answer from rds.<region>.amazonaws.com (or monitoring.…) |
No answer within 10 seconds, no route, a DNS, proxy or TLS failure, or an AWS server error. | Give the collector a route: a NAT gateway, HTTPS_PROXY, or a VPC interface endpoint. Retried every 5 minutes. |
| AWS error — The collector’s clock is off | AWS refused the request time: more than 5 minutes off. | Run NTP on the collector (chrony or systemd-timesyncd). Retried every hour, and on Test again. |
| AWS error — AWS refused the role’s credentials | The role’s credentials were not accepted (invalid or expired). | Check the instance profile, and that the region is enabled for your account. Retried every hour, and on Test again. |
| AWS error — AWS refused the call: see the agent log | Any other refusal. | Read the agent log on the collector (journalctl -u wt15-server-agent): its line database <uuid>: cloud metrics aws_error other (<identifier>, <n> metrics): AWS error <code>, HTTP <status> names AWS’s error code. |
| RDS Proxy | The host is an RDS Proxy: its targets, no metrics of its own. | Add each instance you want to watch. |
On the Cloud metrics card and at step 5, each collector’s Cloud metrics check reads one of these. The active collector’s follows the database’s state above; a standby’s or candidate’s is its own check:
| Check | Fix |
|---|---|
| Reads RDS and CloudWatch · Reads RDS (CloudWatch is checked once it is the standby) | — |
| <action> denied | Attach the policy to that server’s role. A standby that fails adds a warning: If <server> takes over, cloud metrics stop… Engine metrics are not affected. |
| rds:DescribeDBClusters denied: role and replica lag unknown, the rest read | Attach the full policy. |
| No instance role attached · No answer from IMDS | As IMDS unavailable above. |
| Cannot reach rds.<region>.amazonaws.com · Cannot reach monitoring.<region>.amazonaws.com | Give that server a route. |
| Clock off: AWS refused the request time (run NTP) | Run NTP on that server. |
| AWS refused the role’s credentials | Check that server’s instance profile. |
| AWS refused the call: see the agent log | Read that server’s agent log. |
| Throttled by AWS: backing off | None. A standby’s or candidate’s throttled check keeps its last result until the next probe. |
| Reads RDS; this database was not matched | As Not found above. |
| Reads RDS (an RDS Proxy has no CloudWatch metrics) | — |
| Agent too old for cloud metrics | Let its agent update itself. |
| Not checked yet | None: it checks at its next probe. |
Permissions
Section titled “Permissions”database.view reads the Overview’s Capacity view, the Cloud metrics, Instance (from the RDS API) and Collection cards and
the policy. database.edit presses Test again and Add as a database. See
Roles & permissions.
Not yet
Section titled “Not yet”- Other clouds (Google Cloud SQL, Azure and the rest) are not read: cloud metrics are for AWS only.
- Another AWS account: a collector reads only its own account and region.
- CloudWatch Logs and Enhanced Monitoring’s process lists are not read. The slow and error logs are read from the instance’s own log files through the RDS API: see Slow and error logs.