Skip to main content
rightsizing · aws

RDS Multi-AZ on Non-Production Instance

resource types
1
rule IDs covered
1
severity
low

What does ZopNight detect here?

Multi-AZ enabled on non-production RDS instances doubles compute, storage, IOPS, and throughput for a standby dev workloads rarely justify. ZopNight prices the saving as exactly 0.50 of that Multi-AZ-attributable component basis, clamped to half the monthly bill, suppressing when a reserved instance keeps billing regardless and abstaining on cluster members.

Signal and threshold

How ZopNight evaluates RDS Multi-AZ on Non-Production Instance.
Field Value
Rule IDsRC-106
Categoryrightsizing
Severitylow
Metricnone — pure configuration read
Sourcerds_multiaz_nonprod.go

Why cluster members and covered instances are skipped

  • resource.Status: statusAvailable
  • env classification (EV-T0068): prod env-tag is an authoritative veto; fires when non-prod by name or by a dev/test env-tag, not explicitly prod-tagged
  • Metadata[“multi_az”] (JSON bool, discoverer-stamped, and not a Tag; the prior hasTag gate was dead) must be true
  • Not a cluster member (resource.ParentUID == ""): the ÷2 “remove one standby” saving model applies only to a classic single-standby Multi-AZ instance. A Multi-AZ DB cluster is a ~3× two-readable-standby topology priced at the cluster level (#2251), so RC-106 abstains on any cluster member. AWS already reports DBInstance.MultiAZ=false for cluster members (HA is a cluster property signalled by DBClusterIdentifier→ParentUID), so the multi_az gate normally suffices; the ParentUID guard is defense-in-depth so the rec can never mis-fire even if that flag were set.
  • PricingAware: full MonthlyCost (CurrentCostUSD display basis + defensive clamp; no longer the savings basis, see Cost calculation)
  • RDSMultiAZAttributableCostAware: the per-resource monthly sum of the calcRDS components Multi-AZ doubles (compute+storage+iops+throughput), which is the savings basis
  • SetReservationCoverage: per-resource purchase type, which suppresses the rec when it is Reservation or SavingsPlan. An RDS RI is a sunk commitment that keeps billing whether or not Multi-AZ is enabled (it can’t be turned off), so disabling Multi-AZ yields no realizable saving on a covered instance until it expires. Fires on OnDemand/unknown coverage (fail-open, mirroring ec2_heatmap_schedule). Sourced from the aggregator’s CUR-derived ComputedCost.PurchaseType (billing-connected orgs).

Halving only the four components Multi-AZ doubles

the standby saving is priced off the Multi-AZ-attributable component basis alone, never a fraction of the whole bill. SetMultiAZAttributableCost injects basis[uid] = the monthly sum of the calcRDS compute+storage+iops+throughput CostComponents (aggregator’s ComputeResourceCosts → ComputedCost.Components, parsed and daily→monthly projected ×30 by fetchRDSMultiAZAttributableCost in service.go, mirroring fetchLambdaGBSecondCost). calcRDS doubles exactly those four components under Multi-AZ, the same four AWS charges twice for a synchronous standby (calc_aws_database.go: one shared azMultiplier applied to all four). Backup beyond the free tier and data transfer are not doubled, and calcRDS doesn’t model them. So savings = basis × rdsMultiAZStandbyShare (0.50) is exact on the rack-rate (calculated) path and on any billing-actual bill priced at/near rack: it prices only the doubled components and excludes the non-doubled line items (backup, data transfer) a real bill also carries, which a whole-bill×0.5 previously (and wrongly) folded in and overstated. Clamped to half the displayed cost (cost × 0.50, Pricing[uid]): the synchronous standby is structurally ≤ half the total bill (one of two identical instances + its attached storage). On a deeply discounted billing-actual bill (EDP/private rate, PurchaseType still OnDemand so the RI/SP suppression doesn’t fire) the rack basis/2 can exceed the discounted bill’s standby half; the clamp then caps the saving at cost × 0.50, a tight upper bound rather than an exact figure in that case, since cost also carries non-doubled backup/data-transfer, so cost×0.5 can overstate the true removable standby by at most half of those (typically ~0: backups within DB size are free, DB cross-AZ transfer is small). We accept that bounded, rarely-material overshoot rather than abstain on every discounted org (no discounted-component breakdown exists to price the standby exactly). The rule abstains (returns nil) when either Pricing[uid] or multiAZAttributableCost[uid] is absent or ≤ 0, and it never falls back to a fraction of the whole bill.

  • Aggregator wiring: ComputeResourceCosts (aggregator/internal/service/service.go) computes the rack-rate cost.Calculate output unconditionally (both branches), so output.Components is available even when the headline MonthlyCost comes from real billing. The billing-actual branch now also marshals output.Components onto the returned ComputedCost (previously it carried through HourlyRate but dropped Components, which would have made this basis silently absent, and RC-106 permanently abstain, for every billing-connected org).
  • Re-added plumbing: a 2026-07-13 pass (03fb6105d) deleted the prior compute-only ComputeCostAware/fetchRDSComputeCost seam and widened RC-106 to MonthlyCost×0.50. This change supersedes that: a new RDSMultiAZAttributableCostAware interface (rule.go) + fetchRDSMultiAZAttributableCost producer (service.go, wired at the same two HandleDiscovered/refreshRecommendationsSync injection sites as SetPricing) sum all four doubled components (not just compute), fixing the whole-bill overstatement on the billing-actual path while staying exact on the calculated path.
  • Basis accuracy on the gp3 striped baseline: because the standby saving is derived from calcRDS’s iops/throughput components, calcRDS uses AWS’s size/engine-aware gp3 free baseline (gp3Baselines): 3,000 IOPS / 125 MiBps, rising to 12,000 / 500 for striped volumes (≥400 GiB; ≥200 GiB Oracle; SQL Server never stripes). A flat 3,000/125 would have billed IOPS/throughput AWS provides for free on large gp3 volumes, inflating the component basis and, halved, the reported RC-106 saving. See aggregator docs/COST.md → RDS Instances.

Turning off the standby in place

  1. Verify this is a non-production database
  2. Go to RDS Console → Modify → Disable Multi-AZ (rds:ModifyDBInstance, MultiAZ=false); hot in-place, no stop/reboot
  3. Apply during maintenance window
  4. Saving is half the instance’s monthly cost: the standby replica’s compute, storage, IOPS, and throughput are all removed together

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

417 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

417 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·