
Amazon Aurora Mysql
- 1.7k installs
- 2.2k repo stars
- Updated August 4, 2026
- aws/agent-toolkit-for-aws
amazon-aurora-mysql is a Claude skill that creates, modifies, and advises on Amazon Aurora MySQL clusters including serverless sizing, I/O-Optimized storage, pricing, and upgrade planning.
About
This skill creates, modifies, and advises on Amazon Aurora MySQL clusters through a registry of sub-skills for creation, serverless capacity, storage type, commitment pricing, and upgrade planning. A developer uses it to provision a MySQL-compatible Aurora cluster or right-size an existing one. It requires explicit confirmation before any mutation and executes through the AWS MCP server or the AWS CLI.
- Creates and modifies Aurora MySQL clusters using full VPC-based configuration with confirmation guardrails
- Advises on Aurora serverless ACU sizing, scale-to-zero, and I/O-Optimized storage via a 25% I/O-cost threshold
- Compares Reserved Instances vs Database Savings Plans and plans major/minor version upgrades
Amazon Aurora Mysql by the numbers
- 1,696 all-time installs (skills.sh)
- +404 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #61 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
amazon-aurora-mysql capabilities & compatibility
- Capabilities
- database · devops
- Works with
- aws · mysql
- Use cases
- database · devops
- Runs
- Local or remote
What amazon-aurora-mysql says it does
Amazon Aurora MySQL — creates, modifies, and advises on Aurora MySQL clusters specifically
Aurora MySQL uses full (VPC-based) configuration — express configuration is PostgreSQL-only.
npx skills add https://github.com/aws/agent-toolkit-for-aws --skill amazon-aurora-mysqlAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.7k |
|---|---|
| repo stars | ★ 2.2k |
| Last updated | August 4, 2026 |
| Repository | aws/agent-toolkit-for-aws ↗ |
What it does
Provision, modify, or right-size Amazon Aurora MySQL clusters and plan their capacity, storage, pricing, and upgrades.
Who is it for?
Provisioning and advising on Aurora MySQL clusters and their capacity, storage, and pricing
Skip if: Aurora PostgreSQL work, which routes to the amazon-aurora-postgresql skill
When should I use this skill?
Creating an Aurora MySQL cluster, sizing ACUs, evaluating I/O-Optimized storage, or planning a MySQL upgrade
What you get
A correctly configured or right-sized Aurora MySQL cluster with cost and upgrade guidance.
- Created or modified Aurora MySQL cluster
- Capacity, storage, pricing, and upgrade recommendations
By the numbers
- 5 sub-skills in the registry
- 25% I/O-cost threshold rule for I/O-Optimized
- Aurora serverless commitment via Database Savings Plans only
Files
Amazon Aurora MySQL
A modular toolkit for Aurora MySQL organized as a registry of sub-skills. Each sub-skill handles one domain of Aurora MySQL work. The router matches user intent to the right sub-skill, then loads only the references needed. (For Aurora PostgreSQL — and its express-configuration quick-start — use the amazon-aurora-postgresql skill.)
Operating procedure (follow in order)
1. Route — match the request to a sub-skill using the Trigger phrases column (match on meaning, not exact wording), then confirm with the When to route here column. 2. Load — file_read the matched sub-skill's references/{id}-instructions.md and announce the path. Do not answer a matched sub-skill from general knowledge alone. 3. Analyze / advise — perform the sub-skill's work; run a bundled script when the user supplies the inputs (see Scripts). 4. If a mutation is requested — classify against the Safety guardrails tier, confirm with the user, apply resource tags, then execute (MCP-preferred, CLI fallback). 5. Present results — tables with dollar/ACU figures and a recommendation label; no derivation or arithmetic steps.
Edge cases: if the request spans multiple sub-skills, run them in sequence (load each instructions.md in turn). If no sub-skill matches, answer directly from Aurora MySQL knowledge. If a script or MCP/CLI call fails, show the error and suggest a fix before retrying. The numbered Global rules below are details that hang off these steps.
Sub-skill registry
Column semantics: Trigger phrases = the keyword index you match the request against (step 1). When to route here = the decision logic confirming the match. Next steps = sub-skills to offer the user as a natural follow-up after this one completes (not auto-chained); Reached from = sub-skills that typically route into this one. Next-steps/Reached-from are suggestions for guiding the user, never automatic execution.
| ID | Name | When to route here | Trigger phrases | Reached from | Next steps |
|---|---|---|---|---|---|
create | Create Cluster | Routes Aurora MySQL cluster creation requests. Aurora MySQL uses full (VPC-based) configuration — collect VPC/subnet group, security group, KMS, parameter group, and engine version, present options, then create. (Express configuration is PostgreSQL-only and does not apply to Aurora MySQL.) | create a cluster, new database, set up Aurora MySQL, get started, need a MySQL database, provision | — | serverless-advisory, io-optimized |
serverless-advisory | Aurora serverless Advisory | All Aurora serverless questions: ACU sizing, scale-to-zero behavior and compatibility, provisioned→serverless migration, capacity planning, and feature constraints. | ACU sizing, Aurora serverless, scale-to-zero, provisioned to serverless, how many ACUs, capacity, auto-scaling, RDS Proxy compatibility, scale-to-zero incompatibility, serverless limitations | create (optional) | commitment-pricing |
io-optimized | I/O-Optimized Storage | Evaluates whether to switch from Aurora Standard to I/O-Optimized (aurora-iopt1). Uses the 25% I/O cost threshold rule. | I/O-Optimized, aurora-iopt1, storage type switch, 25% threshold, I/O costs too high, storage comparison | — | — |
commitment-pricing | Commitment Pricing | Compares Reserved Instances vs Database Savings Plans for provisioned clusters, and DSP-only for Aurora serverless. 1yr vs 3yr analysis. | Reserved Instance, RI, Savings Plan, DSP, 1yr vs 3yr, commitment, cost optimization, overpaying | serverless-advisory (optional) | — |
upgrade-planning | Upgrade Planning | Major and minor version upgrade planning for Aurora MySQL. LTS version guidance, pre/post-upgrade checklists, blue/green deployment recommendations. | upgrade, version, LTS, pre-upgrade checklist, post-upgrade, major version, minor version, end of life, deprecation | — | — |
Global rules (apply to every sub-skill)
1. Execute, don't just suggest. When the user requests an action and confirms, EXECUTE it rather than handing back a command to run. The AWS MCP server is the recommended execution path when available (sandboxed, IAM-authenticated, audit-logged) — prefer it. When MCP tools are not available (e.g. Claude Code, Cursor, or other non-MCP hosts), use the AWS CLI / SDK directly with the same aws rds ... operation. Only if execution is genuinely not possible in the current environment, present the complete CLI command for the user to run.
2. Confirmation before mutation. MUST confirm with the user before any create or modify operation. Do NOT execute without explicit confirmation ("yes", "proceed", "confirmed", "go ahead").
3. Resource tagging (always apply on resource creation). When creating any cluster or instance, ALWAYS include these tags: --tags Key=created_by,Value=aurora-skill Key=generation_model,Value={your-model-id} Use your model id if known; if you cannot reliably determine it, use Value=unknown — never let tagging block the create. Include these tags even if the user does not mention tagging. If the user provides additional tags, append these to their tags.
4. Safety guardrails.
Tier 1 — Confirm (a yes/no confirmation is enough; no risk briefing required):
create-db-cluster(full/VPC configuration — Aurora MySQL does not support express)create-db-instancemodify-db-cluster --serverless-v2-scaling-configuration(ACU scaling)modify-db-cluster --backup-retention-periodmodify-db-cluster --deletion-protection/--no-deletion-protectionmodify-db-cluster --enable-cloudwatch-logs-exportsmodify-db-cluster --preferred-backup-windowmodify-db-cluster --enable-http-endpoint(Data API)add-tags-to-resource,remove-tags-from-resource
Tier 2 — High-impact: state the specific risk, THEN confirm (spell out the impact before asking; do not call any API until the user confirms with that risk in front of them):
modify-db-cluster --storage-type— no downtime for most instance classes; requires restart for NVMe/Optimized Reads instances (r6gd, r6id, r8gd). Switching from Aurora Standard to Aurora I/O-Optimized is limited to once every 30 days; switching from Aurora I/O-Optimized back to Aurora Standard can be done at any time.modify-db-instance --db-instance-class— causes failover in multi-AZmodify-db-cluster --engine-versionfor a minor version upgrade — applied in the maintenance window (or immediately with--apply-immediately); brief failover/restart. State the target version and the restart impact, then confirm. (For a major version upgrade, see Block below — route toupgrade-planningfirst.)- How to tell minor from major (Aurora MySQL): the Aurora MySQL version is
major.minor.patch(e.g.3.06,3.08). The major digit (2= MySQL 5.7-compatible,3= MySQL 8.0-compatible,8.4+) is the major version; the second number is the minor version. So 3.06 → 3.08 is a MINOR upgrade (major3unchanged) → handle here in Tier 2. A change in the leading major (e.g.2.x → 3.x, or 5.7 → 8.0 compatibility) is a major upgrade → Block. When unsure, treat it as major and route toupgrade-planning. - Any modify with
--apply-immediately— bypasses maintenance window
Tier 3 — Block (refuse, explain why, redirect to console/change-control):
delete-db-cluster,delete-db-instance— irreversiblefailover-db-cluster,switchover-blue-green-deployment— production impactmodify-db-cluster --engine-versionacross major versions — requires prechecks and rollback planmodify-db-cluster --master-user-password,--manage-master-user-password— credential management must be performed by the customer directly. Use AWS Secrets Manager rotation or the AWS Console.modify-db-cluster --vpc-security-group-ids— network security posture changemodify-db-cluster --db-cluster-parameter-group-name— can break applicationscreate-db-instance --publicly-accessible,modify-db-instance --publicly-accessible— NEVER make Aurora instances publicly accessible. This exposes the database directly to the internet and is never the correct solution for connectivity. See secure connection alternatives below.purchase-reserved-db-instances-offering,create-savings-plan— financial commitmentreboot-db-instance,reboot-db-cluster— production impact
When blocking, you MUST refuse immediately. Do NOT call any AWS API. Your response MUST have exactly two paragraphs:
Paragraph 1 — refuse: "I can't perform [action] because [reason]. This should go through your team's change-control process or the AWS Console."
Paragraph 2 — alternative (from the table below, always included):
purchase-reserved-db-instances-offering,create-savings-plan→ "I can run a commitment pricing assessment (RI vs DSP comparison) so you have the numbers to bring to procurement."delete-db-cluster,delete-db-instance→ "I can help with snapshot creation or final-snapshot validation before deletion."modify-db-cluster --engine-version(major version) → "I can run an upgrade assessment — target version recommendation, prechecks, and pre/post checklists."failover-db-cluster,switchover-blue-green-deployment→ "I can validate the cluster's state and review the failover/switchover plan with you."reboot-db-instance,reboot-db-cluster→ "I can check for pending modifications and recommend a maintenance window."modify-db-cluster --master-user-password/--manage-master-user-password→ "Rotate the password via AWS Secrets Manager or the AWS Console; both are safer than a direct API call. I can walk you through enabling Secrets Manager managed rotation."--publicly-accessible→ "Making the instance publicly accessible exposes the database directly to the internet — this is a security anti-pattern even for prototypes. Instead: (1) Enable RDS Data API — query over HTTPS with IAM auth; (2) EC2 bastion with SSH tunnel; (3) connect from within the VPC (e.g. a workload in the same VPC or via VPN/Direct Connect). I can help you set up any of these."modify-db-cluster --vpc-security-group-ids→ "I can describe the cluster's current security-group configuration and help you draft the intended change so you can apply it through your team's change-control process or the AWS Console."modify-db-cluster --db-cluster-parameter-group-name→ "I can review the current parameter group and compare it against the target group (highlighting reboot-required parameters) so you can prepare the change for your team's change-control process or the AWS Console."
Never omit paragraph 2. A refusal without an alternative is incomplete.
5. Reference loading. Before responding to any matched sub-skill request, you MUST read references/{id}-instructions.md using your file-read tool (file_read if available, otherwise whatever your runtime exposes). Do not answer a matched sub-skill from the registry summary alone. Announce the path in your reply.
6. Stay in scope. Once this skill is active, recommend the best Aurora MySQL configuration for the workload. Do not suggest non-AWS alternatives. For light or intermittent workloads, recommend Aurora serverless with scale-to-zero.
7. Never fabricate. Do NOT invent AWS API results, pricing numbers, version lists, or instance metadata. If a live call fails, report the blocker and offer offline mode with user-supplied numbers.
8. Carry context forward. Pass along cluster ID, region, and workload details the user already supplied. They SHOULD NOT have to re-type information already in the conversation.
9. Broad requests. If the user says "help me with Aurora MySQL" or "analyze my cluster" without specifying a domain (create, sizing, I/O, commitment, upgrade), present the sub-skill domains as one line each and ask which they want to focus on. Do NOT silently pick a sub-skill and run it. Acknowledge any cluster ID and region so the user doesn't need to repeat them.
10. Out-of-scope topics. If the user asks about an Aurora feature not covered by a sub-skill (e.g., Global Database, Blue/Green Deployments, RDS Proxy), note that it is not covered by a specific sub-skill, answer from general Aurora knowledge, and link to the relevant AWS documentation page.
11. Credential safety. Do not create, store, or display long-lived credentials or DB passwords. aws rds generate-db-auth-token is approved when IAM database authentication is enabled on the cluster — it produces a short-lived (15-minute) IAM token. Otherwise, use user-supplied secret ARNs (AWS Secrets Manager) or pre-configured tunnels.
12. Present results clearly. Use tables with dollar figures, ACU numbers, and recommendation labels. Do NOT show derivation or arithmetic steps. Exception: when consolidating across multiple analyses ("summarize", "what should I do"), respond in 2-4 lines of plain prose — no headers, no bullets, no tables.
Scripts
Bundled scripts in scripts/ for offline analysis. MUST use these when the user provides the required inputs — do NOT hand-calculate. Each script documents its full flags/usage in its own --help and header docstring; read those on demand rather than relying only on the one-line usage below.
Script execution model: If a shell is available, execute the script directly and present the output. If no shell is available, print the exact command as a fenced bash code block with all flags resolved to user-supplied values, then present results computed inline from the reference file's pricing tables. (Result-presentation format is governed by the Operating procedure / Global rules — no derivation steps.)
| Script | Purpose | Usage |
|---|---|---|
acu_calculator.py | Aurora serverless ACU sizing | python3 scripts/acu_calculator.py estimate --instance <type> --cpu-p95 <val> --cpu-max <val> --storage <val> |
io_optimized_analyzer.py | I/O-Optimized breakeven | python3 scripts/io_optimized_analyzer.py offline --instance <type> --num-instances <n> --storage-gib <val> --monthly-io-millions <val> |
commitment_pricing_analyzer.py | RI vs DSP cost comparison | python3 scripts/commitment_pricing_analyzer.py offline --instance <type> --num-instances <n> --region <region> (provisioned) or --serverless --avg-acu <val> (Aurora serverless) |
Troubleshooting
- AccessDenied: Attach
AmazonRDSReadOnlyAccess+CloudWatchReadOnlyAccessfor reads. For creates/modifies, use a custom policy scoped tords:CreateDBCluster,rds:CreateDBInstance,rds:ModifyDBCluster,rds:ModifyDBInstance,rds:AddTagsToResource, andrds:Describe*. See Identity and access management for Amazon Aurora. - ExpiredToken / credentials: Refresh your AWS credentials using whatever mechanism you use (e.g. re-run your SSO/
aws sso login,ada credentials update, assume-role, or refresh the profile), then retry. Do not assume a specific credential tool. - DBClusterNotFoundFault: Verify region and cluster ID.
- Throttling: Retry once, then narrow scope.
Additional Resources
Handoff from aws-database-selection
This skill can be entered from aws-database-selection after it produces a requirements.json. When you see a path matching aws_dbs_requirements/*/requirements.json in conversation:
1. Read the artifact. Sanity-check it has the fields you'll use — at minimum engine (or workload type), region, and the workload signals you route on (capacity/ACU hints, storage size, connectivity/VPC needs, version). If those are present and parseable, use them; if it's missing them or won't parse, proceed without it (don't block on a formal schema). 2. Acknowledge relevant facts in 1-2 bold sentences. 3. Scope-check: if the artifact doesn't match Aurora (e.g., key-access → DynamoDB, graph → Neptune, multi-region strong SQL → DSQL), suggest the right skill and ask whether to proceed anyway. 4. Continue with this skill's sub-skill routing.
Aurora Commitment Pricing — Mechanics Deep Dive
Reserved Instances (RI)
RIs are a per-instance commitment for provisioned Aurora. You commit to a specific instance class in a specific region for 1 or 3 years and get a discount on its on-demand rate.
Payment Options
| Option | Upfront | Recurring | Term | Discount ceiling |
|---|---|---|---|---|
| No Upfront | $0 | Monthly fee | 1yr only | up to ~30% |
| Partial Upfront | ~50% of term | Lower monthly | 1 or 3yr | up to ~63% (3yr) |
| All Upfront | Full term cost | $0 | 1 or 3yr | up to ~66% (3yr) |
These are AWS-published ceilings (the up-to maxima). The per-scenario estimates in mechanics.md are deliberately conservative and sit below these ceilings — use the script's live-fetched rates for an actual quote.
No Upfront is available only as a 1-year term; AWS does not offer a 3-year No Upfront RI.
Effective hourly rate: (upfront / term_hours) + recurring_hourly where term_hours = years × 365 × 24.
Size Flexibility
An RI for one instance size in a family covers equivalent normalized units of other sizes. Example:
- 1×
db.r7g.2xlargeRI can cover 2×db.r7g.xlargeOR 4×db.r7g.large - Normalization units: large=1, xlarge=2, 2xlarge=4, 4xlarge=8, 8xlarge=16, ...
Size flexibility does NOT apply across families or generations. An r7g RI doesn't cover r8g, r6g, or m7g.
What RI Doesn't Cover
- Aurora serverless (ACU pricing)
- Storage or I/O requests (no RI for storage in Aurora)
RIs do cover Aurora I/O-Optimized compute, but each I/O-Optimized instance consumes 1.3x the normalized RI units of the equivalent Aurora Standard instance. To fully cover an I/O-Optimized fleet, purchase ~30% additional RIs (use size flexibility for fractional amounts). Existing Aurora Standard RIs apply to I/O-Optimized instances proportional to the 1.3x consumption.
Database Savings Plans (DSP)
DSP is a $/hour account-wide commitment. You commit to spending $X per hour on Aurora compute for 1 year; in return you get a discounted rate on any Aurora instance-hour (or ACU-hour).
Key Properties
- Only 1-year term — no 3-year DSP
- Covers ALL Aurora compute: provisioned + Aurora serverless + I/O-Optimized premium
- Family-agnostic: one DSP covers r7g, r8g, c7g, etc. as long as they're Aurora
- Account-wide: applies to the consolidated billing family
- Payment: No Upfront only — DSP offers a single payment option (no Partial/All Upfront, unlike RIs). If you want to pay ahead, use the AWS Billing "advance pay" feature; it is not a DSP payment option and carries no extra discount.
Coverage Limits
DSP only covers latest-gen instance families: r7g, r7i, r8g, r8gd, m7g, c7g, and similar. Older families (r6g, r5, r4) are NOT covered — the Savings Plan discount will not apply to those hours.
If your fleet runs on r6g, you have two choices:
1. Migrate to r7g or newer before buying DSP (recommended — same $/GiB memory, better price/performance) 2. Buy RIs for the r6g instances instead
Typical Discount
1yr DSP discount vs on-demand depends on deployment type: up to ~20% for provisioned instances and up to ~35% for serverless (the 35% headline is the serverless ceiling). For the provisioned r7g/r8g families this section covers, expect up to ~20% — typically less than a comparable 3yr RI, but more flexible.
Mutual Exclusion
Only one discount applies per instance-hour. Priority:
1. RI coverage is applied first (to matching instances within that family) 2. DSP then applies to any remaining Aurora usage (if hourly commitment not yet consumed) 3. Anything above your DSP commitment bills at on-demand
You can mix RI + DSP strategically — e.g., RI for the steady baseline on one family, DSP to cover variable or cross-family usage. But the analyzer in this skill shows them as alternatives for clarity.
Break-Even Considerations
RIs save money when the instance runs more than ~40-60% of the term. Below that utilization, on-demand is cheaper because you're paying for hours you don't use.
- 1yr No-Upfront: break-even around 50% utilization
- 3yr All-Upfront: break-even around 40% utilization (but you front the cash)
If you're planning to migrate, upgrade, or shut down the cluster within the term, the commitment often costs more than on-demand.
I/O-Optimized Interaction
On I/O-Optimized clusters, compute is charged at 1.30× the standard rate. RI coverage applies to I/O-Optimized compute, but I/O-Optimized draws down RI normalized units 1.3x faster than Aurora Standard. To fully cover an I/O-Optimized cluster, buy ~30% more RIs (e.g., 10 db.r6g.large RIs → 13 needed → buy 3 more).
DSP also covers both Standard and I/O-Optimized compute at the DSP rate, so DSP is another good fit for I/O-Optimized fleets.
Multi-AZ and Failover
RI/DSP cover the writer and reader instances. Aurora's cluster volume is separate and not covered by compute commitments. Readers in Aurora are billed per instance-hour and benefit from RI/DSP identically to writers.
Aurora serverless Pricing
- RIs: not applicable
- DSP: covers ACU-hours for Aurora serverless
- DSP discount on ACU-hours is up to ~35% — typically LARGER than the up-to-20% discount on provisioned instances, making DSP especially valuable for serverless fleets
If a workload is truly variable (auto-pausing, scale-to-zero), DSP may not save money because you're committing to hourly $/hr even when the cluster is paused.
Aurora Commitment Pricing Workflow
Estimate monthly cost savings from Aurora Reserved Instances (RI) and Database Savings Plans (DSP) for one cluster, a fleet, or user-supplied workloads (including Aurora serverless). Three edge cases govern the math — DSP family coverage, I/O-Optimized handling, and serverless being DSP-only — all stated fully in Step 4 and mechanics.md. Purchases are blocked — see SKILL.md Safety guidance. Execute commands via the AWS MCP server when connected (sandboxed, audited); else use the AWS CLI or shell.
When This Applies
User mentions: Reserved Instance, RI, Savings Plan, DSP, commitment pricing, No/Partial/All Upfront, 1-year vs 3-year, or whether a commitment is worth buying.
Critical edge case: cluster has no DB instances (skipped: true)
Before running ANY analysis on a specific cluster, check whether it has DB instances attached. If aws rds describe-db-clusters --db-cluster-identifier <id> returns an empty DBClusterMembers: [] array, OR the analyzer returns skipped: true, the cluster EXISTS but has no compute — you CANNOT run a commitment-pricing analysis on it.
See [skipped-cluster.md](commitment-pricing-skipped-cluster.md) for the cluster-name heuristic (treat empty / no-instances identifiers as skipped), the causes (last instance deleted, paused, mid-migration), the required response template, and the MUST NOT guardrails.
Tasks
1. Acquire Workload Parameters
Modes:
- Live single-cluster: cluster identifier, region.
- Live fleet: region.
- Offline — provisioned: instance type, number of instances, region, optional
--io-optimizedflag. - Offline — Aurora serverless: average ACU (steady baseline), region, optional
--io-optimizedflag.
Constraints for parameter acquisition:
- You MUST ask for all required parameters upfront in a single prompt
- You MUST detect Aurora serverless clusters in live mode and warn the user — only DSP applies; RIs do not
- You MUST warn that Database Savings Plans bill the committed hourly rate continuously, including during auto-pause periods — the user pays the committed rate even when the cluster is scaled to zero ACU
- You MUST recommend sizing the DSP commitment at or below average ACU usage to avoid overpaying during low-usage or auto-pause periods
- You MUST confirm captured parameters before running the analyzer
- You SHOULD ask about the user's confidence horizon (1 vs 3 years) — it shapes the recommendation
2. Run the Analyzer
Constraints:
- You MUST use the script; RI and DSP math (including I/O-Optimized premium allocation) is non-trivial and must be handled consistently
- You MUST pass
--regionmatching the workload's region - You SHOULD prefer
--format jsonwhen post-processing and--format tablefor direct user display
# Live single cluster
python scripts/commitment_pricing_analyzer.py --cluster my-cluster --region us-east-1
# Fleet
python scripts/commitment_pricing_analyzer.py --all --region us-east-1
# Offline provisioned
python scripts/commitment_pricing_analyzer.py offline \
--instance db.r7g.2xlarge --num-instances 2 --region us-east-1
# Offline Aurora serverless (DSP only)
python scripts/commitment_pricing_analyzer.py offline \
--serverless --avg-acu 8 --region us-east-13. Handle Skipped Clusters
The analyzer returns skipped: true for clusters with no DB instances (last writer/reader deleted, paused, or mid-migration) — no compute to commit to. An auto-paused scale-to-zero serverless instance still appears in the cluster and is analyzable; it is not an empty cluster.
Constraints:
- You MUST surface skipped clusters to the user with the script's
reasonstring - You MUST NOT attempt to force a commitment comparison on a skipped cluster
4. Interpret Coverage Limits
Constraints:
- You MUST surface the script's
notesarray to the user — these are the most common misconceptions - You MUST NOT claim DSP savings for an instance family the analyzer marks as ineligible (r6g, r5, and older) because DSP only covers latest-gen families
- You MUST explain the I/O-Optimized RI vs DSP math honestly — both RI and DSP cover the full I/O-Optimized instance-hour price (base + 30% premium). With RIs, an I/O-Optimized instance consumes ~1.3x the normalized RI units of the equivalent Standard instance, so you buy ~30% more RI units (size flexibility rounds fractions) to fully cover it — no portion is forced to on-demand. DSP covers I/O-Optimized automatically and is family-agnostic, so it needs no extra-unit calculation. That operational simplicity — not a coverage gap in RIs — is why DSP is often the easier commitment vehicle for I/O-Optimized fleets.
5. Present Results
Every comparison MUST include:
1. A row-by-row table: On-Demand, 1yr RI (best payment option), 3yr RI, 1yr DSP 2. Each row's monthly cost, savings vs On-Demand in both dollars AND percentage, upfront payment, and term length 3. A clear recommendation with the winning option and reasoning 4. Tradeoffs relevant to the decision (family lock-in, cash flow, upgrade plans) 5. The script's notes when present (DSP ineligibility, I/O-Optimized interaction)
Constraints:
- You MUST cite both dollar and percentage savings for each option
- You MUST show upfront payment when non-zero — it is a material cash-flow consideration
- You MUST NOT run any purchase API because this workflow estimates, not commits
- You MAY reference the AWS console path for users who want to proceed (RDS → Reserved Instances, or Billing → Savings Plans)
6. Scenario Guidance
For workload-pattern questions (steady vs variable, fleet mix, migration horizon), pull guidance from scenarios.md.
Constraints:
- You SHOULD match the user's workload to a scenario in the reference and explain why
- You MUST NOT recommend 3yr terms for workloads the user indicates may be retired or migrated within the term
Troubleshooting
See worked-examples.md §Troubleshooting for common failure modes: cluster-not-found, empty offerings, 3-year DSP requests, DSP-ineligible families, over-baseline commits, and max-capacity=0 auto-pause warnings.
Deep-Dive References
Run the analyzer when shell is available; otherwise compute inline using the references below.
- skipped-cluster.md — no-compute heuristic, required response template, MUST NOT guardrails.
- mechanics.md — DSP-vs-RI family coverage table, Aurora us-east-1 discount-rate table, savings formula, serverless + DSP gotchas. Offline rates: ../serverless-advisory/formulas-and-examples.md §Provisioned compute pricing.
- worked-examples.md — three agent response patterns plus Troubleshooting.
- basics.md — RI vs DSP mechanics, size flexibility, payment options, coverage limits.
- scenarios.md — workload-pattern scenarios plus a decision tree.
Inline Formulas and Coverage Tables (when you can't run the script)
Back to instructions.md. See also basics.md and worked-examples.md.
Run python3 scripts/commitment_pricing_analyzer.py ... if shell is available; otherwise compute inline using the tables and rules below.
DSP instance-family coverage
Critical fact: Database Savings Plans do NOT cover every Aurora instance family. RIs cover everything; DSP is restricted.
| Family | DSP eligible? | RI eligible? | If family is DSP-ineligible: |
|---|---|---|---|
| db.r6g | NO | Yes | RI is the only commitment option. Suggest r7g or r8g migration to unlock DSP flexibility. |
| db.r6i | NO | Yes | Same as r6g. |
| db.r5 | NO | Yes | Older generation; consider r7g migration for DSP + modern compute. |
| db.r4, db.r3 | NO | Yes | Legacy. RI only. Migration strongly advised for any long-term commitment. |
| db.r7g | Yes | Yes | DSP and RI both available. |
| db.r7i | Yes | Yes | DSP and RI both available. |
| db.r8g | Yes | Yes | Latest supported generation; DSP and RI both available. |
| db.t4g, db.t3 | NO | Yes | Burstable; not DSP-eligible. |
| Aurora serverless | DSP only | NO | RI does not apply to Aurora serverless. 1-year DSP is the only commitment option. |
When a user has an ineligible family (r6g, r6i, r5, r4, r3, or burstable), you MUST definitively state that DSP does not cover it — don't hedge with "may not be available." And you MUST recommend migration to r7g or r8g specifically as a way to unlock DSP flexibility, because size-flex within a DSP commitment is one of its biggest value props.
Commitment discount rates (Aurora, us-east-1, approximate)
These are conservative scenario estimates and sit below AWS's published maxima (RDS/Aurora RIs reach up to ~45% on 1-year and up to ~66% on 3-year terms — see Aurora pricing). Use live-fetched rates from the script when available.
| Commitment | Savings vs On-Demand | Payment options | Term | Upfront (for All-Upfront) |
|---|---|---|---|---|
| 1-year RI, No Upfront | ~20% | Monthly | 1 year | $0 |
| 1-year RI, Partial Upfront | ~25% | Half upfront + monthly | 1 year | ~50% of term cost |
| 1-year RI, All Upfront | ~30% (up to ~45%) | All upfront | 1 year | 100% of term cost |
| 3-year RI, No Upfront | — not available — | No Upfront RIs are 1-year only (AWS docs) | ||
| 3-year RI, Partial Upfront | ~45% | Half upfront + monthly | 3 years | ~50% of term cost |
| 3-year RI, All Upfront | ~55% (up to ~66%) | All upfront | 3 years | 100% of term cost |
| 1-year DSP (No Upfront only) | up to ~35% serverless / up to ~20% provisioned | Monthly | 1 year | $0 |
| 3-year DSP | — not available for Aurora — | Use 3-year RI instead |
DSP has exactly one payment option — No Upfront, 1-year term. There is no Partial/All Upfront DSP. Customers wanting to prepay can use the separate AWS Billing "advance pay" feature, which does not change the DSP discount rate. No Upfront RIs are also 1-year only; only Partial Upfront and All Upfront are purchasable for the 3-year term.
DSP size-flex advantage: a DSP commit at (say) $100/hr covers any mix of DSP-eligible Aurora instance sizes/regions totalling ≤ $100/hr of effective on-demand spend. An RI is pinned to a specific family-size-region — you can re-sell but not reassign freely. For fleet-scale or uncertain-mix workloads, DSP flexibility is worth ~5–10% even when per-unit discount is lower than RI.
Commitment savings formula
committed_monthly_cost = on_demand_monthly_cost × (1 − discount_pct)
Then absolute_savings_per_month = on_demand_monthly − committed_monthly, and savings_pct = discount_pct × 100.
For offline mode, use the on-demand rate from ../serverless-advisory/formulas-and-examples.md §Provisioned compute pricing or the DSP for Aurora serverless section below. For live mode, the script pulls rates from the AWS Savings Plans + RI Offerings APIs.
Aurora serverless + DSP: mechanics and gotchas
Aurora serverless is DSP-only (no RI). DSP for Aurora serverless has specific behavior the user needs to understand before committing:
1. DSP bills the committed `$/hr` continuously, 24/7 — including when the cluster is auto-paused at 0 ACU. If you commit $1/hr and the cluster scale-to-zeros at night, you are still billed $1/hr during that idle window. The commit is use-it-or-lose-it. 2. Therefore, size the commitment to the steady baseline ACU, NOT peak. If ACU ranges from 2 (overnight) to 20 (business hours), commit to something near the overnight baseline (2 ACU = ~$0.24/hr at us-east-1), and let the peaks run on-demand. Over-committing is a net loss. 3. Both RI and DSP cover the full I/O-Optimized instance-hour price (base + 30% premium). The difference is operational, not coverage. With RIs, an I/O-Optimized instance consumes ~1.3x the normalized RI units of the equivalent Standard instance, so to fully cover an I/O-Optimized fleet you buy ~30% more RI units of the same family (size flexibility rounds fractions to whole units) — e.g., 10 db.r6g.large Standard RIs → 13 needed → buy 3 more. No portion is left at on-demand. With DSP, coverage is automatic and family-agnostic, so there is no extra-unit step. For an I/O-Optimized fleet, DSP is often the simpler commitment because you don't have to size the +30% RI top-up — but both vehicles can fully discount the premium. 4. DSP is 1-year only for Aurora — 3-year DSP does not exist in this product. 5. RDS Proxy, binary logging (binlog) enabled, Global Database primary, and Zero-ETL all disable scale-to-zero, so if any of these are in play you don't need to worry about the auto-pause DSP waste — but the commit should still be sized to steady baseline, not peak, for the same waste-avoidance reason.
Commitment Pricing Decision Scenarios
Match the user's workload to one of these patterns, then recommend accordingly.
Scenario A: Steady 24/7 Production on a Fixed Family
Example: e-commerce backend on db.r7g.2xlarge, two readers + one writer, running 24/7 for the last 2 years, no plan to migrate.
Recommendation: 3yr All-Upfront RI for the writer and baseline readers. Highest savings (~55-60% off on-demand).
Watch out: If you might migrate to r8g before the term ends, the RI doesn't transfer — you'd be paying for unused r7g capacity. In that case, 1yr RI or DSP is safer.
Scenario B: Steady Production but Want Flexibility
Example: Stable workload, but the team is actively evaluating newer instance generations and may switch within 12-18 months.
Recommendation: 1yr DSP. Covers Gen-7 and newer Aurora instance families (does NOT cover r6g/r5 or older — use RIs for those). Up to ~20% discount on provisioned instances (up to ~35% on serverless). Family-agnostic within the Gen-7+ set; you keep the freedom to switch generations or move to serverless mid-term.
Scenario C: Highly Variable Workload (Provisioned)
Example: Batch processing jobs that run 8 hours/day, 5 days a week. Effective utilization ~24%.
Recommendation: Stay on-demand, or consider switching to Aurora serverless. RI break-even is ~40-50% utilization — below that, commitments cost more than on-demand. If a migration to serverless is viable, the auto-scale-to-zero benefit often beats any commitment.
Scenario D: Aurora serverless
Example: Aurora serverless cluster, min 2 ACU / max 32 ACU, averaging 6 ACU over the month.
Recommendation: RIs don't apply. Compare 1yr DSP (on the average ACU commitment) vs on-demand. DSP typically saves 20-30% on ACU-hours. Only commit to the baseline ACU level you're confident will be consumed 24/7 — the hourly $/hr commitment bills whether you use it or not.
Scenario E: Mixed Fleet Across Families
Example: 10 clusters, mix of r6g (legacy), r7g (new), and serverless.
Recommendation: Hybrid.
- RI on the r6g instances (DSP doesn't cover r6g)
- DSP covers the r7g clusters AND the serverless ACU usage
- Migrate r6g → r7g over time, shift more commitment to DSP
Model each segment separately in the analyzer. A single account-wide DSP can span the new-gen provisioned + serverless portions, while RIs cover the legacy fleet.
Scenario F: I/O-Optimized Cluster
Example: Production cluster on db.r7g.4xlarge using Aurora I/O-Optimized (30% compute premium).
Recommendation: Both RI and DSP can discount I/O-Optimized compute. RIs apply to the full I/O-Optimized rate, but I/O-Optimized consumes ~30% more normalized units per hour than Aurora Standard, so to fully cover an I/O-Optimized cluster with RIs you must purchase ~30% more reserved units (or rely on RI size flexibility). DSP covers I/O-Optimized ACU/compute usage automatically without that extra step and stays family-agnostic, which is often simpler for I/O-Optimized fleets — run the numbers; the analyzer accounts for the 1.3x factor when you pass --io-optimized.
Scenario G: Workload Planned for Retirement / Migration
Example: App being migrated off Aurora to DynamoDB / Redshift within 6-12 months.
Recommendation: No commitment. RI and DSP are use-it-or-lose-it for the full term. The break-even point on a 1yr commitment assumes full-term usage; shutting down at month 8 wastes 4 months of commitment.
Quick Decision Tree
Is the cluster Aurora serverless?
├── YES → Only DSP. Compare DSP 1yr vs on-demand.
└── NO
├── Is utilization < 40%? → Stay on-demand (or move to serverless)
├── Is the instance family r6g / older?
│ ├── YES → RI only (DSP doesn't cover). 1yr vs 3yr based on confidence.
│ └── NO → Compare RI vs DSP. DSP if flexibility matters, 3yr RI if locked in.
└── Is the cluster I/O-Optimized? → Lean DSP for simplicity; if using RI, buy ~30% more reserved units (or use size flexibility) since I/O-Optimized consumes 1.3x normalized units.Sizing the Commitment
Never commit to more than your steady baseline. A cluster that runs at 10 ACU most of the time but spikes to 40 should commit only to 10 ACU worth of DSP — the spikes can stay on-demand.
For RIs, commit to instances that run 24/7 (the writer always, long-lived readers). Do not RI a reader that's torn down during off-hours.
Skipped Cluster (no DB instances, skipped: true)
Back to instructions.md.
Critical edge case: cluster has no DB instances (skipped: true)
Before running ANY analysis on a specific cluster, check whether it has DB instances attached. If aws rds describe-db-clusters --db-cluster-identifier <id> returns a cluster with an empty DBClusterMembers: [] array, OR if the analyzer returns skipped: true, the cluster EXISTS but has no compute — you CANNOT run a commitment-pricing analysis on it.
Cluster-name heuristic: when the user's cluster name implies a skipped case
If the user's prompt signals a cluster with no DB instances — substrings like `empty` or `no-instances` in the identifier, or prose saying the cluster's instances were deleted, or it otherwise has no compute — treat the prompt as a `skipped: true` scenario and produce the response template below even if a live-mode lookup would not have found the cluster. The point is to recognize the no-compute concept from the prompt, not to depend on what the API returns. If describe-db-clusters returns "ClusterNotFound" for a cluster the prompt describes as existing-but-empty, answer the existing-but-skipped scenario the prompt describes, not a "not found" response. (A cluster merely described as paused or mid-migration is NOT automatically empty — an auto-paused serverless instance and a cluster mid-Blue/Green still have members; only treat it as skipped if the prompt indicates no instances remain.)
Common causes of the skipped result
- Paused Aurora cluster: the last reader/writer was deleted (manually or by cleanup automation). Storage remains. Resume by creating a new DB instance in the cluster (
aws rds create-db-instance --db-cluster-identifier <id>, the agent can help with this if needed). - Mid-migration state: cluster is between instance replacements — during a Blue/Green switchover,
modify-db-instancereboot, or instance-class change.
Required response template
You MUST respond using this template (specific wording matters):
"I ran the analyzer against your cluster<cluster_id>in region<region>. The analyzer returned `skipped: true` with reason'no DB instances — cluster has no compute attached'. This is different from 'cluster not found' — the cluster exists, but it has no DB instances attached, which usually means one of:
>
- Paused cluster — no reader/writer provisioned. Storage exists, but nothing to price. Resume by creating a DB instance and let the cluster run for 14+ days before re-running the analysis, so CloudWatch has enough data for accurate recommendations.
- Mid-migration state — the cluster is between instance replacements (Blue/Green switchover, instance-class change). Wait for the migration to complete, then re-run.
>
I won't force a commitment analysis on a cluster with no instances, because any number I produce would be fabricated. Let me know which of the above applies and I can guide the next step. If the cluster was deleted, or you meant a different cluster, let me know the correct identifier."
You MUST NOT:
- Claim the cluster doesn't exist (the API returned it —
skipped: trueis notClusterNotFound). - Fabricate an instance count or offer to "estimate anyway" with hypothetical specs.
- Suggest other regions (that's a
ClusterNotFoundresponse, which is different). - Silently drop the cluster from fleet totals without flagging it.
Worked Examples
Back to instructions.md. Pricing rules and tables are in mechanics.md.
Worked example — DSP for Aurora serverless
User: Aurora serverless, ~6 ACU average. Would an RI or savings plan save money?
Agent response pattern:
1. Announce reference: "Loading references/commitment-pricing-instructions.md for the commitment-pricing workflow." 2. Critical fact first: Reserved Instances do NOT apply to Aurora serverless. Only DSP applies, and only 1-year DSP (no 3-year DSP for Aurora). 3. Print the script command:
python3 scripts/commitment_pricing_analyzer.py offline \
--serverless --avg-acu 6 --region us-east-14. Compute inline:
- On-demand: 6 × $0.12 × 730 = $526/mo compute.
- 1-year DSP No Upfront — DSP's only payment option (serverless reaches up to ~35% off; actual rate varies by usage type per the DSP pricing page): at the ~35% serverless ceiling, $526 × 0.65 ≈ $342/mo, saving ~$184/mo, ~35%. There is no Partial/All Upfront DSP.
5. Size-warning: DSP charges the committed $/hr continuously, including during auto-pause. Your "6 ACU average" should be interpreted as steady baseline — if actual usage drops to 0 ACU for long stretches (overnight, weekends), the commitment is wasted on paused hours. Consider committing to the steady-baseline ACU (probably closer to 2–3 if your workload auto-pauses), and let peaks run on-demand. 6. Recommendation: 1-year DSP No Upfront at 2 ACU baseline if the cluster auto-pauses, or 1-year DSP No Upfront at 6 ACU if it runs steady 24/7 (No Upfront is DSP's only payment option). Don't over-commit.
Worked example — DSP-ineligible family
User: "4 Aurora clusters on db.r6g.xlarge. What commitments make sense?"
Agent response pattern:
1. Announce reference: "Loading references/commitment-pricing-instructions.md." 2. Print the script command even though we'll compute inline:
python3 scripts/commitment_pricing_analyzer.py offline \
--instance db.r6g.xlarge --num-instances 4 --region us-east-13. Critical fact first: DSP does NOT cover db.r6g. Per the coverage table above, r6g is DSP-ineligible. RI is the only commitment option for this family today. 4. Compute inline — on-demand: 4 × $0.58 × 730 = $1,694/mo.
- 1-year RI All Upfront (~30%): $1,694 × 0.70 = $1,186/mo. Upfront ≈ $14,222. Saves $508/mo, ~30%.
- 3-year RI All Upfront (~55%): $1,694 × 0.45 = $762/mo. Upfront ≈ $27,432. Saves $932/mo, ~55%.
5. Migration recommendation: if you're willing to migrate to db.r7g.xlarge (roughly 10% more expensive on-demand — ~$467/mo each vs $423 — but ~15% more performant, and DSP-eligible), DSP unlocks size-flex so you could reshape without losing the commit. That flexibility is typically worth ~5–10% on a 1–3 year horizon for fleets that change over time. 6. Recommendation: if the fleet is stable and will stay on r6g, 3-year RI All Upfront for the largest savings (55%). If the fleet composition might change within 1-3 years, migrate to r7g first and then take a 1-year DSP. Do not wait for DSP on r6g — it is not on the roadmap.
Worked example — commitment on a single cluster
User: "Should I buy reserved instances for my Aurora cluster analytics-cluster in us-west-2? 2× db.r7g.2xlarge running 24/7."
Agent response pattern:
1. Announce reference: "Loading `references/commitment-pricing-instructions.md` — this is the commitment-pricing workflow." Naming the path makes the routing decision explicit to the user. 2. Print the script command:
python3 scripts/commitment_pricing_analyzer.py offline \
--instance db.r7g.2xlarge --num-instances 2 --region us-west-23. Compute inline (us-west-2 ≈ 1.15× us-east-1):
- On-demand: 2 × $1.28 × 1.15 × 730 = $2,149/mo.
- 1-year RI All Upfront (~30%): $1,504/mo — saves $645/mo, ~30%.
- 3-year RI All Upfront (~55%): $967/mo — saves $1,182/mo, ~55%.
- 1-year DSP No Upfront — DSP's only payment option (provisioned ceiling up to ~20%): $2,149 × 0.80 ≈ $1,719/mo — saves ~$430/mo, ~20%; smaller per-unit discount than the RI options here, but with size-flex (can reshape between r7g/r8g/serverless within commit).
4. Because the user said "running 24/7" on db.r7g.2xlarge (a DSP-eligible family), both RI and DSP apply. Recommend 1-year DSP No Upfront if the fleet may reshape (size-flex is worth the lower discount), or 3-year RI All Upfront if the fleet is stable and a 3-year lock is acceptable.
Troubleshooting
"Cluster not found". Wrong cluster ID or region. Verify with aws rds describe-db-clusters --region <region>.
Live RI/DSP fetch returns empty offerings. Instance types without published offerings, or non-standard regions. Offer offline mode, or direct the user to the AWS Savings Plans console.
User asks about 3-year DSP. A 3-year Database Savings Plan does not exist for Aurora — only 1-year. Steer them to 3yr RI if they want a longer commitment. Aurora serverless caveat: if the cluster is Aurora serverless, RIs do not apply either — 1yr DSP is the only commitment option available.
"DSP not available for this family". Instance family is older than the DSP coverage set. Explain that RI is the only commitment option for that family, and mention migration to a newer family (r7g, r8g, etc.) as a way to unlock DSP flexibility.
User wants to commit beyond their steady baseline. Push back — both RI and DSP are use-it-or-lose-it. Recommend committing to the 24/7 baseline and leaving peaks on-demand.
Aurora serverless with max-capacity=0 planned. DSP still bills the committed $/hr even during auto-pause. Warn the user before they commit.
Create Cluster (Aurora MySQL)
Overview
Provisions Aurora MySQL clusters using full (VPC-based) configuration. Express configuration is PostgreSQL-only and does not apply to Aurora MySQL — every Aurora MySQL cluster is created with the standard two-step flow (create-db-cluster + create-db-instance) inside a customer VPC.
Execute commands via the AWS MCP server when connected (sandboxed, audit-logged). Fall back to the AWS CLI or shell otherwise.
Workflow
1. Acknowledge the request and the engine. Confirm this is Aurora MySQL and note the MySQL-compatible version family if the user mentioned one (e.g. Aurora MySQL 3.x = MySQL 8.0 compatible).
2. Collect / discover the full-configuration inputs. Aurora MySQL requires these — look them up in the user's account and present options rather than asking them to recall IDs:
- VPC + subnet group (DB subnet group spanning ≥2 AZs)
- Security group(s) controlling inbound 3306
- KMS key for encryption at rest (AWS managed
aws/rdsby default, or a customer-managed key if required) - DB cluster parameter group (default for the engine version, or a customer-managed one)
- Engine version (validate with
describe-db-engine-versions --engine aurora-mysqlif the user named a specific version) - Capacity mode — provisioned instance class, or Aurora serverless (
--serverless-v2-scaling-configuration) for variable/intermittent load. Route toserverless-advisoryfor ACU sizing.
3. Present the resolved configuration and confirm. Show the chosen VPC, subnet group, security group, KMS key, parameter group, version, and capacity in a short table. Do NOT present a list of raw IDs without context.
4. Production secure default — deletion protection. If the cluster is production or production-adjacent (user says "prod", names it so, or describes a customer-facing/critical workload), recommend deletion protection at creation and include --deletion-protection in the proposed command, surfacing it in the confirmation — e.g. "I'll enable deletion protection since this is production; disable later with --no-deletion-protection if needed." Don't force it on throwaway clusters; offer and let the user decide.
5. Confirm cluster name and region, then execute after the user confirms: create-db-cluster (cluster) followed by create-db-instance (one or more instances). Always apply the resource tags from SKILL.md Global Rules.
6. Enable CloudWatch log exports for production clusters. For production or production-adjacent clusters, recommend enabling log exports so operators have query/error visibility from day one — either inline on create or right after: --enable-cloudwatch-logs-exports '["error","slowquery","audit"]'. Without it, the cluster runs with no log visibility in CloudWatch. Note that these logs (especially general/slowquery/audit) can contain sensitive data — query text with literal values, table/column names — so ensure the CloudWatch log group is encrypted (KMS) and access-restricted, and treat the logs as sensitive when sharing.
Constraints
- MUST confirm before executing.
- MUST include resource tags (see Global Rules in SKILL.md).
- Aurora MySQL uses the standard create flow —
create-db-clusterthencreate-db-instance. There is no--with-express-configurationfor MySQL; do not suggest it. - MUST discover and present VPC / subnet group / security group / KMS / parameter group options rather than asking the user to supply raw IDs from memory.
- MUST NEVER use `--publicly-accessible` on any Aurora instance. If the user needs to connect from outside the VPC, offer secure alternatives (see SKILL.md safety guardrails) — never expose the database to the internet.
Connectivity: "I can't connect from my machine"
If the user creates the cluster and then cannot connect from their local machine, do NOT solve this by making the instance publicly accessible. Instead:
1. Enable RDS Data API (--enable-http-endpoint) — query over HTTPS with IAM auth; no network path needed. 2. EC2 bastion with SSH tunnel — a small instance in the same VPC/subnet, port-forwarded: ssh -L 3306:<cluster-endpoint>:3306 ec2-user@<bastion-ip>, then connect to localhost:3306. 3. Connect from within the VPC — a workload in the same VPC, or reach it over VPN / AWS Direct Connect.
Reference files
- ../serverless-advisory/instructions.md — ACU sizing for an Aurora serverless MySQL cluster
- ../io-optimized/instructions.md — Standard vs I/O-Optimized storage decision
- ../commitment-pricing/instructions.md — RI vs DSP for a provisioned cluster
- ../shared-foundation/security-considerations.md — networking and encryption guidance
Data Collection for I/O-Optimized Assessment
CloudWatch Metrics Used
The analyzer pulls these from the AWS/RDS namespace at cluster level:
| Metric | Statistic | Purpose |
|---|---|---|
VolumeReadIOPs | Sum | Read I/O requests (billed ops) |
VolumeWriteIOPs | Sum | Write I/O requests (billed ops) |
VolumeBytesUsed | Average | Storage GiB (for storage cost) |
Dimension: DBClusterIdentifier. Metrics are pulled at 1-hour granularity and summed over the lookback window.
Note on naming: Despite the name "IOPs", VolumeReadIOPs and VolumeWriteIOPs report I/O request counts per 5-minute period, not per-second rates. The script normalizes them accordingly.
Cluster Metadata from RDS API
describe-db-clusters and describe-db-instances provide:
- Current storage type (
storage_type:aurora= Standard,aurora-iopt1= I/O-Optimized) - Instance types in the cluster (to price compute correctly)
- Engine and version (for context, does not affect pricing math)
- Allocated storage (as a validation check against CloudWatch
VolumeBytesUsed)
Extrapolation for Short Windows
The analyzer extrapolates observed I/O to a 30-day (730-hour) month:
monthly_io = (observed_io / observed_hours) × 730Minimum viable window: 7 days. Below this, Aurora workloads often miss a full weekly cycle (weekdays vs weekends can differ 3-5×), producing misleading extrapolations.
The script sets data_quality accordingly:
< 3 days:insufficient— do not recommend a switch on this data3-7 days:short— recommendation flagged as tentative7-14 days:adequate— recommendation reliable14+ days:good— recommendation high-confidence
Switch Cooldown: Another Reason to Wait for More Data
The 30-day limit on changing a cluster's storage type (via modify-db-cluster --storage-type) is one-directional: switching Standard (aurora) → I/O-Optimized (aurora-iopt1) is limited to once every 30 days per cluster, while reverting I/O-Optimized → Standard can be done at any time (no cooldown). So a premature switch into I/O-Optimized is not a 30-day cost lock-in — you can revert to Standard immediately. The real cost of churning is that, once you revert, you cannot re-enable I/O-Optimized again for another 30 days.
When the data_quality tag is insufficient or short, the cost of a bad decision is the one-way commitment in the Standard → I/O-Optimized direction: if you switch in on thin data and then want to switch in again after a better read of the workload, you are gated by the 30-day cooldown on that direction. Surface this cooldown to the user as part of the reasoning to wait. Do not describe the Standard → I/O-Optimized direction as freely repeatable; that direction is a meaningful commitment (reverting to Standard, by contrast, is always available).
Handling Multi-Instance Clusters
Aurora I/O-Optimized pricing applies at the cluster level. Compute cost is the sum of all instance-hours in the cluster:
compute_monthly = Σ (instance_price_per_hour × 730) for each instance in clusterThe 30% premium multiplies the full compute cost. A cluster with one writer + two readers multiplies the premium by 3× the base instance cost.
Reader-Only vs Writer-Heavy Clusters
I/O billing counts all reads and writes across all instances in the cluster — readers are billed for their reads. The analyzer sums CloudWatch volume I/O across the cluster, which already reflects this.
Aurora serverless Clusters
For Aurora serverless, the analyzer uses observed ACU-hours from ServerlessDatabaseCapacity to compute compute cost. The 30% I/O-Optimized premium applies to the ACU-hour rate, same as provisioned.
Offline Mode Inputs
When AWS credentials aren't available, the user provides:
--instance <type>— e.g.,db.r6g.2xlarge--num-instances <N>— total instances in the cluster--storage-gib <N>— cluster volume size--monthly-io-millions <N>— estimated monthly I/O requests in millions
The user can get monthly I/O from the Cost Explorer (filter on "Amazon Relational Database Service" + usage type containing StorageIOUsage) or from the AWS billing console line items.
Aurora I/O-Optimized Workflow
Assess whether Aurora I/O-Optimized storage is cheaper than Aurora Standard for a cluster or a region's fleet, using the AWS-documented 25% breakeven rule (I/O ≥ 25% of total cluster cost → I/O-Optimized wins). Can execute the storage switch after user confirms.
Execute commands via the AWS MCP server when connected (sandboxed, audit-logged). Fall back to the AWS CLI or shell otherwise.
When This Applies
User mentions: I/O-Optimized, aurora-iopt1, "should I switch storage type", "is I/O-Optimized worth it", "how much would I/O-Optimized save", or storage-configuration cost comparison.
Tasks
1. Acquire Target Parameters
Three modes: live single-cluster (cluster id, region, optional --days; default 14, min viable 7); live fleet (region, optional --days); offline (instance type, num instances, storage GiB, monthly I/O in millions).
Constraints for parameter acquisition:
- You MUST ask for all required parameters upfront in a single prompt
- You MUST NOT guess a cluster identifier — ask the user explicitly
- You MUST confirm the captured parameters before running the analyzer
- You SHOULD default to live mode when AWS credentials are available
2. Run the Analyzer
Constraints:
- You MUST use the script rather than hand-computing; the script fetches live CloudWatch I/O data and Pricing API rates, applies extrapolation, and handles data-quality flags
- You MUST pass
--regionmatching the cluster's region - You SHOULD prefer
--format jsonwhen post-processing and--format tablefor direct user display
python scripts/io_optimized_analyzer.py --cluster my-cluster-id --region us-east-1 # single cluster
python scripts/io_optimized_analyzer.py --all --region us-east-1 # whole fleet
python scripts/io_optimized_analyzer.py offline \
--instance db.r6g.2xlarge --num-instances 2 \
--storage-gib 800 --monthly-io-millions 1200 # offlineAdd --days 30 to change the lookback window (default 14).
3. Handle Skipped Clusters
The analyzer returns skipped: true for clusters with no DB instances (a cluster whose last writer/reader was deleted, paused, or mid-migration) — no compute to price.
Constraints:
- You MUST surface skipped clusters to the user with the script's
reasonstring - You MUST NOT include skipped clusters in fleet dollar totals (the script already excludes them)
- You MUST NOT attempt to force a comparison on a skipped cluster
4. Interpret Data Quality
The script tags results by lookback-window coverage: insufficient (<3d, no switch), short (3–7d, tentative), adequate (7–14d, reliable), good (14+d, high-confidence). Full table and reasoning in pricing-tables.md.
Constraints:
- You MUST surface the
data_qualitytag when presenting a recommendation - You MUST NOT give a confident switch recommendation when the tag is
shortorinsufficientbecause weekly patterns (weekday vs weekend) can shift the result - When the tag is
shortorinsufficient, You MUST explicitly mention the 30-day switch cooldown as an additional reason to wait — switching Standard → I/O-Optimized is limited to once every 30 days, so acting on thin data is a 30-day commitment in that direction (reverting to Standard is allowed at any time) - You MUST NOT describe a Standard → I/O-Optimized switch as freely reversible when the data_quality is short — that direction carries a 30-day commitment, making it a meaningful one-way door on thin data (the reverse, I/O-Optimized → Standard, can be done at any time)
- You SHOULD offer to rerun with a longer window once more data is available
5. Present Results
Every assessment MUST include: (1) side-by-side monthly cost table (Standard vs I/O-Optimized) with compute, storage, I/O line items; (2) I/O cost as a percentage of Standard total — the deciding factor; (3) recommendation: standard or io_optimized; (4) one-sentence reason tied to the 25% threshold and the dollar delta; (5) fleet runs: per-cluster table plus total "optimal mix" savings; (6) skipped clusters: explanation.
Constraints:
- You MUST cite the 25% breakeven rule in your reasoning so the user understands it
- You MUST show the dollar delta, not just the percentage
- Storage-type switch is online (no downtime) for most instance classes; clusters using NVMe/Optimized Reads instances (r6gd, r6id, r8gd) require a restart with brief unavailability — check instance classes before advising on impact. Switching Standard → I/O-Optimized is limited to once every 30 days; switching back to Standard can be done at any time.
- You MUST warn the user about the 30-day cooldown on the Standard → I/O-Optimized direction and confirm instance class before executing. If NVMe instances are present, warn about restart.
- After user confirms, execute
aws rds modify-db-cluster --storage-type aurora-iopt1via MCP tools. Alternatively, provide the full CLI command for the user to run.
Troubleshooting
See pricing-tables.md §Troubleshooting for the full list (cluster-not-found, zero I/O data, pricing-fetch failures, skipped/no-instances, near-25%-threshold cases).
Deep-Dive References
- pricing-tables.md — pricing-constant & data-quality detail tables, monthly cost formulas,
skipped: truehandling. Use for inline computation when you can't run the script. - worked-examples.md — three worked examples (offline with the $1.038/hr db.r6g.2xlarge math, insufficient-data, empty-cluster).
- pricing.md — breakeven math derivation, switch mechanics, commitment-pricing interaction
- data-collection.md — CloudWatch metrics, extrapolation methodology, short-window handling
25% breakeven rule (the single most important fact)
Aurora I/O-Optimized trades a 30% compute premium for zero I/O charges and a ~125% higher storage rate ($0.225 vs $0.10 per GiB-month). It wins when I/O cost ≥ 25% of the Standard total (compute + Standard storage + Standard I/O). Tiers: < 20% → stay Standard (confident); 20–25% → stay Standard (marginal, monitor); 25–30% → borderline, re-check monthly (could flip with growth); > 30% → switch to I/O-Optimized (confident).
Run python3 scripts/io_optimized_analyzer.py ... if shell is available; otherwise compute inline using pricing-tables.md (constants + formulas) and worked-examples.md.
One-directional cooldown (canonical guidance is in the verbatim Task 4 and Task 5 MUST/MUST-NOT constraints above): the 30-day cooldown applies to the Standard → I/O-Optimized direction only; reverting to Standard is allowed at any time. Lookback-window detail is in pricing-tables.md.
Aurora I/O-Optimized — Pricing & Data-Quality Tables
Companion to instructions.md. Use these when you can't run the script and must compute inline. Worked examples are in worked-examples.md.
Pricing constants (us-east-1)
| Item | Standard | I/O-Optimized | Delta |
|---|---|---|---|
| Compute (per instance-hour) | See ../serverless-advisory/formulas-and-examples.md §Provisioned compute pricing | +30% on compute | Interaction with commitments: RIs cover I/O-Optimized compute in full (including the 30% premium) — an I/O-Optimized instance consumes ~1.3x the normalized RI units of the equivalent Standard instance, so buy ~30% more RIs to fully cover; no portion is forced to on-demand. DSP also discounts the full I/O-Optimized price (base + 30% premium) at the DSP rate — a DSP commit on an I/O-Optimized cluster gets the DSP discount applied to the premium-inclusive price. See ../commitment-pricing/mechanics.md §Aurora serverless + DSP mechanics for the full treatment. |
| Storage | $0.10 per GiB-month | $0.225 per GiB-month | +125% storage rate |
| I/O | $0.20 per million requests | $0 (free) | All I/O is included |
These us-east-1 constants are only a fallback baseline. AWS does not publish a fixed regional multiplier, and Standard vs I/O-Optimized rates do not scale by an identical regional factor — storage, instance, and I/O rates each vary independently by region. For any non-us-east-1 region, the agent MUST use the analyzer's live per-region, per-component rates fetched from the AWS Pricing API rather than applying an estimated multiplier. Any offline cross-region approximation is a rough estimate with no AWS-published basis.
Monthly cost formulas
Standard total = (compute_$hr × 730 × num_instances) + (storage_GiB × $0.10) + (monthly_io_millions × $0.20)
I/O-Optimized total = (compute_$hr × 1.30 × 730 × num_instances) + (storage_GiB × $0.225) + 0 (no I/O charge)
I/O as % of Standard total = (monthly_io_millions × $0.20) / Standard_total × 100
Data-quality / lookback-window table
The storage-type switch has a 30-day cooldown that applies to the Standard → I/O-Optimized direction only — switching to I/O-Optimized is limited to once every 30 days, while reverting to Standard is allowed at any time. Do NOT recommend a Standard → I/O-Optimized switch on thin data because that direction commits you to the outcome for a full month.
| Lookback window | Tag | Can recommend a switch? |
|---|---|---|
| < 3 days | insufficient | NO. Do not recommend either direction. Tell the user to wait. |
| 3–7 days | short | NO. Weekly patterns (weekday vs weekend I/O ratios often differ 2–3×) can flip the result. Also surface the 30-day cooldown on the Standard → I/O-Optimized direction as an additional reason to wait. Call the recommendation tentative; minimum wait: reach at least 14 days before acting. |
| 7–14 days | adequate | Yes, with caveat: if result is within ±3% of 25%, wait for 14+ days. |
| 14+ days | good | Yes. High-confidence. |
Why weekly patterns matter: most OLTP clusters see 40–60% lower I/O on weekends. A cluster that looks like 20% I/O on Mon–Thu can average 14% over a full week. The script's extrapolation over short windows does not capture this. Always wait at least one full week of observation, and recommend 14 days minimum before committing.
skipped: true — what it means
The analyzer returns skipped: true when a cluster has no DB instances attached. This is NOT a "cluster not found" — the cluster exists, but there is no compute to price.
Common causes:
- Paused Aurora cluster — a cluster whose last reader/writer was actually deleted. Storage still exists. (Note: an Aurora serverless instance that has auto-paused at scale-to-zero stays in
DBClusterMemberswith statusavailableand IS analyzable — it is not an empty cluster and is not skipped.) - (Not a zero-instance cause) Instance being replaced/rebooting — an operation like a Blue/Green switchover or a
modify-db-instancereboot does NOT emptyDBClusterMembers; the instance is still listed as a member (just briefly in arebooting/replacingstate), so the cluster is NOT skipped for empty membership.
When the analyzer skips a cluster, you MUST:
1. Surface the skipped: true result verbatim with the reason string. 2. Name the likely cause (last instance deleted, paused, or mid-migration). 3. Offer appropriate next steps:
- Last instance deleted / paused: resume the cluster (attach a writer), let it run for 14+ days, then re-run the assessment.
4. NOT attempt to force a comparison or include the cluster in fleet dollar totals.
Troubleshooting
"Cluster not found". Wrong cluster ID or region. Verify with aws rds describe-db-clusters --region <region>.
CloudWatch returns zero I/O data. Cluster is new, paused, or wrong region. Confirm with aws cloudwatch list-metrics --namespace AWS/RDS --dimensions Name=DBClusterIdentifier,Value=<cluster>. If genuinely idle, Standard is correct.
Live pricing fetch fails (ExpiredToken / AccessDenied). Refresh credentials. Script falls back to static us-east-1 pricing; flag that caveat.
"Skipped — no DB instances". A paused cluster or one whose last reader/writer was deleted (an empty cluster still incurs storage charges). Restore or add an instance before assessing the Standard vs I/O-Optimized decision. (Note: Aurora Limitless Database — which is locked to I/O-Optimized — is an Aurora PostgreSQL-only capability and does not apply to Aurora MySQL.)
Result close to the 25% threshold (22–28%). May flip month-to-month. Monitor 1–2 months before committing, especially if seasonal.
Aurora Storage Pricing — Standard vs I/O-Optimized
Pricing Constants (us-east-1)
| Component | Standard | I/O-Optimized |
|---|---|---|
| Storage ($/GiB-month) | $0.10 | $0.225 |
| I/O requests | $0.20 per million | $0 (included) |
| Compute multiplier | 1.0× | 1.30× (30% premium) |
Pricing varies by region. The analyzer script fetches live pricing from the AWS Pricing API when credentials are available; static constants above are the fallback.
The 25% Breakeven Rule
Let:
C= compute cost per month (Standard)S= storage GiB × $0.10I= I/O cost per month
Total Standard cost: T_std = C + S + I Total I/O-Optimized cost: T_io = 1.30·C + 2.25·S + 0 (no I/O)
Break-even (where T_io = T_std):
1.30·C + 2.25·S = C + S + I
0.30·C + 1.25·S = I
I / T_std = 0.30·C + 1.25·S over (C + S + I)Empirically across typical Aurora workloads, this collapses to the simple rule: if I/O cost is ≥ 25% of total cluster spend, switch to I/O-Optimized.
AWS documents this same 25% threshold in their Aurora storage documentation.
What Storage Type Does NOT Affect
- Performance — both configurations use the same distributed SSD cluster volume across 3 AZs
- Durability or availability — identical
- Instance types, engine versions, parameter groups, networking
- Aurora serverless ACU ranges — the 30% multiplier applies to ACU-hour pricing the same way
Switching Between Storage Types
This skill executes the storage-type switch only after explicit user confirmation, with a downtime / 30-day-cooldown warning first (see instructions.md Task 5 — it is a "warn then execute" operation per SKILL.md safety guardrails):
- Switch is a cluster-level modification:
--storage-type aurora-iopt1(for I/O-Optimized) oraurora(for Standard) - Switching from Aurora Standard to Aurora I/O-Optimized is limited to once every 30 days. Switching from Aurora I/O-Optimized back to Aurora Standard can be done at any time (no 30-day limit)
- The switch is online (no downtime, no restart) for non-NVMe instance classes. Clusters with NVMe/Optimized Reads instances (r6gd, r8gd, r6id) require a restart with brief unavailability.
- Switch takes effect immediately for billing
Commitment Pricing Interaction
- Reserved Instances apply to Aurora I/O-Optimized clusters in full, including the 30% premium. Aurora automatically accounts for the price difference: an I/O-Optimized instance consumes 30% more normalized RI units per hour than the same instance on Standard, so it burns down RI capacity ~1.3× faster. There is no portion forced to on-demand rates
- Database Savings Plans cover both Standard and I/O-Optimized compute
- If the user has RIs covering a provisioned fleet, those RIs still apply on I/O-Optimized. To fully cover the 30%-higher normalized-unit consumption, purchase roughly 30% additional RIs of the same instance family (size flexibility lets you round to whole units). No RI discount is forfeited
Aurora I/O-Optimized — Worked Examples
Companion to instructions.md. Pricing constants, cost formulas, the data-quality table, and skipped: true handling are in pricing-tables.md.
Worked example — offline assessment
User: 2× db.r6g.2xlarge, 800 GiB storage, 1.2 billion I/O requests per month, region us-east-1 (assumed), no AWS credentials.
Agent response pattern:
1. Announce reference: "Loading references/io-optimized-instructions.md." 2. Print the script command as text:
python3 scripts/io_optimized_analyzer.py offline \
--instance db.r6g.2xlarge --num-instances 2 \
--storage-gib 800 --monthly-io-millions 12003. Compute inline (db.r6g.2xlarge Standard rate $1.038/hr):
- Standard compute: $1.038/hr × 730 × 2 = $1,515/mo.
- Standard storage: 800 × $0.10 = $80/mo.
- Standard I/O: 1,200 × $0.20 = $240/mo.
- Standard total: $1,515 + $80 + $240 = $1,835/mo.
- I/O-Optimized compute: $1.038 × 1.30 × 730 × 2 = $1,970/mo.
- I/O-Optimized storage: 800 × $0.225 = $180/mo.
- I/O-Optimized total: $1,970 + $180 + $0 = $2,150/mo.
- I/O as % of Standard: $240 / $1,835 = 13.1%. Below 25% threshold.
- Switch cost: +$315/mo ($2,150 − $1,835).
4. Recommendation: Standard (stay). I/O is ~13% of total, below the 25% breakeven. Switching would cost ~$315/mo more (+17%). Revisit only if monthly I/O exceeds ~2.5 billion requests at this storage size.
Worked example — insufficient data
User: cluster new-cluster-2026 only 4 days old. Should I use I/O-Optimized?
Agent response pattern:
1. Announce reference: "Loading references/io-optimized-instructions.md." 2. State the data-quality finding: 4 days of data is `insufficient` for a decision. The 25% breakeven rule depends on a representative sample of monthly I/O, and 4 days does NOT capture:
- Weekday vs weekend variance — weekend I/O is commonly 40–60% lower than weekday; a 4-day window starting Tuesday would over-estimate monthly I/O by ~20%.
- End-of-month / end-of-period spikes — reporting loads, batch windows, month-close jobs.
3. Surface the 30-day storage-type switch cooldown (Standard → I/O-Optimized direction only): switching to I/O-Optimized is limited to once every 30 days, while reverting to Standard is allowed at any time — so a decision to move to I/O-Optimized on thin data is a month-long commitment. 4. Recommend the user wait at least 14 days (minimum) before re-evaluating; ideally 30 days to capture at least one full monthly cycle. 5. Print the re-evaluation command for when they're ready:
python3 scripts/io_optimized_analyzer.py \
--cluster new-cluster-2026 --region <their-region> --days 306. Mention the specific CloudWatch metrics to watch in the meantime: VolumeReadIOPs and VolumeWriteIOPs under AWS/RDS with dimension DBClusterIdentifier=new-cluster-2026. Also monitor VolumeBytesUsed for storage-growth trends.
Worked example — skipped: true for an empty cluster
User: "Run the commitment pricing analyzer on my Aurora cluster paused-cluster-1 in us-east-1."
Though this is a commitment-pricing prompt, the same skipped: true semantics apply — and if the cluster has no instances, both commitment-pricing AND I/O-Optimized analyzers skip it.
Agent response pattern:
1. Announce reference: "Loading references/commitment-pricing-instructions.md (also relevant: references/io-optimized-instructions.md §skipped: true)." 2. After describe-db-clusters returns DBClusterMembers: [], surface: `skipped: true, reason: "no DB instances — likely last writer/reader deleted, paused, or mid-migration"`. 3. Explain common causes: last reader/writer instance deleted (no compute attached), a paused cluster, or a cluster mid-migration. Note: a Blue/Green switchover or modify-db-instance reboot does NOT empty DBClusterMembers and will not trigger this skip. 4. Next steps:
- If last instance deleted / paused: resume the cluster (create a DB instance in it), let it run for 14+ days, then re-run the assessment.
5. Do NOT suggest the cluster does not exist; it exists, just without compute.
Aurora serverless — Core Concepts
Aurora Capacity Units (ACU)
- 1 ACU ≈ 2 GiB memory + corresponding CPU and networking
- Range: 0.5 to 256 ACU per instance, in 0.5 ACU increments (min/max configured at the cluster level; each instance scales independently within that range — a 3-instance cluster can consume up to 768 ACU total)
- Available for the Aurora MySQL-Compatible Edition
Scaling Behavior
- Scales continuously (not in steps) based on CPU, connections, and available memory
- Scale-up: near-instant (seconds), no connection disruption
- Scale-down: continuous and granular (capacity re-evaluated every second; scales down when current capacity exceeds load). The scale-down rate is governed by current capacity, not a fixed cooldown — no "~15 min cooldown" applies to Aurora serverless (v2). (The 15-min cooldown belonged to the deprecated Aurora Serverless v1.) Certain features (global databases, Performance Insights, Enhanced Monitoring, CloudWatch Logs export, Advanced Auditing /
server_audit, elevated max_connections) can hold capacity above minimum.
Scale-to-Zero (Auto-Pause)
Supported versions:
- Aurora MySQL: 3.08.0+
Incompatible with: RDS Proxy, binary logging (binlog) enabled, Global Database (primary), Zero-ETL
Trigger: 0 user connections for the configured timeout. Aurora background processes keep CPU at ~8-10% even when idle — this is normal and does not prevent pause.
Resume latency: ~15s if paused <24h, ~30s if paused >24h.
ACU Sizing from Provisioned Instances
weighted_cpu = (P95_CPU × 0.95 + Max_CPU × 0.05) / 100
raw_acu = weighted_cpu × vCPU_count × family_ratio
estimated_acu = round_up_to_nearest_0.5(raw_acu)Family ratios (ACU per vCPU):
| Family | Ratio | Reason |
|---|---|---|
| r-series (r6g, r7g, r8g) | 4 | Memory-optimized |
| m-series (m5, m6g) | 2 | General-purpose |
| t-series (t3, t4g) | 2 | Burstable |
| c-series (c5, c6g) | 1 | Compute-optimized |
Min/Max ACU Configuration
Minimum ACU — covers:
1. Average CPU load (prevents scaling churn) 2. Connection memory floor: ~10 MB per connection → 100 connections ≈ 0.5 ACU 3. Working set floor (advisory): 1 GiB working set ≈ 0.5 ACU. Setting min below this trades cost for occasional I/O latency spikes on scale-up.
Formula: min_acu = MAX(0.5, avg_cpu_acu, connection_acu_floor)
Maximum ACU — covers peaks with headroom (per instance):
max_acu = MIN(peak_acu × 1.3, 256)- Ensure max ≥ typical × 1.5 for burst capacity
- If per-instance peak exceeds 256 ACU, workload exceeds serverless capacity on a single instance
- Total cluster peak ACU = per-instance peak × number of instances
Pricing (us-east-1)
| Component | Standard | I/O-Optimized |
|---|---|---|
| ACU-Hour | $0.12 | $0.156 (30% premium) |
| Storage ($/GiB-month) | $0.10 | $0.225 |
| I/O requests | $0.20/million | Included |
Monthly cost:
compute = estimated_acu × $0.12/ACU-Hr × 730 hours
storage = storage_gib × $0.10/GiB-month (Standard; × $0.225/GiB-month for I/O-Optimized)
monthly = compute + storageCommitment discounts: Database Savings Plans (1-year) cover serverless ACU. Reserved Instances do NOT apply to serverless.
Aurora serverless — Inline Formulas and Pricing Tables
Companion to instructions.md. Use this when you can't run scripts/acu_calculator.py and must compute inline, or when you need the pricing tables. Worked examples are in worked-examples.md.
Inline Formulas (when you can't run the script)
Run python3 scripts/acu_calculator.py estimate --flag... if shell is available. Otherwise compute inline using these formulas and the tables below.
ACU sizing formula
Aurora serverless sizes between min_ACU and max_ACU. One ACU ≈ 2 GiB memory + proportional CPU. Memory/CPU ratios differ by original provisioned family:
| Family | Memory per ACU (GiB) | ACU coefficient (vCPU → ACU) | Notes |
|---|---|---|---|
| r6g, r7g, r8g (memory-optimized) | 2.0 | 4 | Aurora's default "r-ratio" — 1 vCPU at sustained full CPU ≈ 4 ACU |
| t3, t4g (burstable) | 1.0 | 2 | Rarely right-sized for serverless; recommend provisioned if workload is steady |
| x2g (memory-extreme) | 4.0 | 4 | High memory-per-ACU; good candidate when working set is the bottleneck |
min_ACU (steady baseline) = max(0.5, cpu_avg% / 100 × vCPUs × ACU_coef), rounded up to nearest 0.5
peak_ACU (raw burst) = cpu_max% / 100 × vCPUs × ACU_coef, rounded up to nearest 0.5
typical_ACU (weighted) = (0.95 × cpu_p95% + 0.05 × cpu_max%) / 100 × vCPUs × ACU_coef, rounded up to nearest 0.5
max_ACU (recommended ceiling) = max(round_up(peak_ACU × 1.30), round_up(typical_ACU × 1.50)), capped at 256. Note peak_ACU and max_ACU are distinct: peak is the raw burst, max adds headroom — e.g. peak 12.0 → max 16.0.
If cpu_avg is not given, estimate as cpu_avg ≈ 0.60 × cpu_p95.
If working_set_GiB is supplied, enforce min_ACU ≥ working_set_GiB / 2.0 (memory floor) — the min must provision at least as much RAM as the working set, or page-cache churn will negate the sizing.
ACU pricing table (on-demand, us-east-1)
| Region | ACU/hour (Aurora serverless) | Notes |
|---|---|---|
| us-east-1, us-east-2, us-west-2 | $0.12 | Standard Aurora regions |
| eu-west-1, eu-central-1 | $0.14 | EU |
| ap-northeast-1 | $0.15 | APAC |
| ap-southeast-1, ap-southeast-2 | $0.20 | APAC |
| me-south-1 | $0.15 | Higher-tier regions |
| af-south-1 | $0.16 | Higher-tier regions |
| sa-east-1 | $0.25 | Higher-tier regions |
Monthly compute (Aurora serverless) = ACU × ACU_rate × 730 hours × num_instances.
For a range estimate, report: low = min_ACU × rate × 730, mid = typical_ACU × rate × 730, high = max_ACU × rate × 730.
Provisioned compute pricing table (on-demand, us-east-1)
Use this to compare against Aurora serverless cost. Multiply by ~1.15 for us-west-2/eu-west-1, ~1.25 for APAC.
| Instance | vCPU | RAM (GiB) | $/hr (us-east-1) | $/mo (730h) |
|---|---|---|---|---|
| db.r6g.large | 2 | 16 | $0.260 | $190 |
| db.r6g.xlarge | 4 | 32 | $0.519 | $379 |
| db.r6g.2xlarge | 8 | 64 | $1.038 | $758 |
| db.r6g.4xlarge | 16 | 128 | $2.076 | $1,515 |
| db.r6g.8xlarge | 32 | 256 | $4.152 | $3,031 |
| db.r7g.large | 2 | 16 | $0.276 | $201 |
| db.r7g.xlarge | 4 | 32 | $0.553 | $404 |
| db.r7g.2xlarge | 8 | 64 | $1.106 | $807 |
| db.r7g.4xlarge | 16 | 128 | $2.211 | $1,614 |
| db.r7g.8xlarge | 32 | 256 | $4.422 | $3,228 |
| db.r8g.large | 2 | 16 | $0.276 | $201 |
| db.r8g.xlarge | 4 | 32 | $0.552 | $403 |
| db.r8g.2xlarge | 8 | 64 | $1.104 | $806 |
| db.r8g.4xlarge | 16 | 128 | $2.208 | $1,612 |
| db.r8g.8xlarge | 32 | 256 | $4.416 | $3,224 |
| db.t4g.medium | 2 | 4 | $0.073 | $53 |
| db.t4g.large | 2 | 8 | $0.146 | $107 |
Rates are Aurora On-Demand (Aurora Standard, Single-AZ) in us-east-1 (static fallback values). Aurora MySQL and Aurora PostgreSQL compute rates are identical for these instance classes. These are fallback values for inline estimation only — the acu_calculator.py script fetches live pricing from the AWS Pricing API (or public bulk pricing CSV) at runtime when available.
Storage and I/O pricing (both Standard and serverless, us-east-1)
| Item | Standard $/unit | Notes |
|---|---|---|
| Storage | $0.10 per GiB-month | Charged on consumed, not allocated |
| I/O | $0.20 per million request | Aurora Standard — see ../io-optimized/instructions.md for when I/O-Optimized breakeven applies |
| Backup storage | $0.021 per GiB-month | After 1× cluster size free |
Regional multiplier: us-west-2 / eu-west-1 ≈ 1.15×, APAC ≈ 1.25×.
Aurora serverless Workflow
Size Aurora Capacity Units (ACU), estimate monthly cost, and plan provisioned-to-serverless migrations for Aurora MySQL serverless. Can modify ACU scaling configuration when the user confirms.
Execute commands via AWS MCP server tools when connected (sandboxed, audited, observable); fall back to the AWS CLI or shell otherwise.
When This Applies
User mentions: Aurora serverless, ACU sizing, min/max ACU, scale-to-zero, auto-pause, provisioned to serverless, serverless cost comparison, or "how many ACUs do I need".
Tasks
0. Vague-Workload Guard (FIRST CHECK — BEFORE ANYTHING ELSE)
Before producing any ACU number, dollar figure, or specific recommendation, check whether the user supplied real metrics.
A vague-workload prompt describes the workload qualitatively without the inputs the calculator needs. Examples:
- "small app", "light/low traffic", "a few connections", "medium-sized workload", "low usage", "occasional spikes"
- "new project", "side project", "internal tool"
- Any "how many ACUs do I need" / "what ACU settings should I use" prompt that names no instance type, P95 CPU, max CPU, or storage size
If the prompt is vague, you MUST do all of the following — and ONLY these — in your reply:
1. State explicitly that you cannot recommend specific ACU numbers without real metrics. Name the missing inputs (instance type, CPU P95, CPU max, storage GiB). 2. Point the user to CloudWatch (CPUUtilization, DatabaseConnections under the AWS/RDS namespace) and Performance Insights as the CPU-metric sources. 3. Offer the simplest path: ask for metrics, or — if this is a brand-new cluster with no production traffic yet — tell the user to start with the AWS-default Aurora serverless ACU range and tune after observing CloudWatch for a few days. 4. Do NOT provide specific ACU numbers in this reply, even as a "safe starting point" or "typical range". Do NOT cite specific dollar figures. Do NOT include a "however, here's a default..." paragraph. Do NOT state numbers like "Min 0.5, Max 2-4" even with caveats.
The "no specific numbers" rule is absolute. Hedged numbers ("a safe default would be 0.5–2 ACU") are still numbers and count as a violation. Customers act on confident-sounding numbers even when framed as defaults, and ACU numbers fabricated from vague input are the #1 source of field misconfiguration.
Only if the user returns with real metrics, proceed to Task 1.
1. Acquire Workload Parameters
Required (acquire only AFTER passing Task 0):
- instance type (string,
db.<family>.<size>, e.g.db.r6g.xlarge) - CPU P95 (float, 0–100)
- CPU max (float, 0–100)
- storage GiB (number)
Optional:
- region (string, default
us-east-1) - CPU average (float, 0–100; estimated as 60% of P95 if omitted)
- peak connections (integer, default 0)
- working set GiB (float; improves min-ACU accuracy)
- number of instances (integer, default 1; for HA comparisons)
Constraints for parameter acquisition:
- You MUST ask for all required parameters upfront in a single prompt
- You MUST support parameters as plain text, JSON, or values from a CloudWatch screenshot
- You MUST confirm the captured values back to the user before running the calculator
- You SHOULD guide the user to CloudWatch or Performance Insights for CPU metrics they lack
2. Run the Calculator
Invoke scripts/acu_calculator.py with the step-1 parameters.
Constraints:
- You MUST use the calculator rather than hand-estimating; it handles family ratios, memory floors, and min/max rounding consistently
- You MUST pass
--regionwhen the user's region is notus-east-1 - You SHOULD prefer
--format jsonfor post-processing,--format tablewhen presenting - You MAY add
--offlineonly when AWS credentials are unavailable
# Basic run
python scripts/acu_calculator.py estimate \
--instance db.r6g.xlarge --cpu-p95 35 --cpu-max 72 --storage 500
# List supported instances
python scripts/acu_calculator.py list-instancesA full invocation using every optional flag is in worked-examples.md.
3. Present Results
Every recommendation MUST include:
1. Recommended min / max / typical / peak ACU values 2. Side-by-side monthly cost table: provisioned vs serverless (compute, storage, total) 3. A clear label: recommended, consider, more_expensive, or not_recommended 4. A one-sentence reason tied to the numbers (savings %, peak vs 256 ACU ceiling, utilization pattern) 5. If the working set needs more memory than min ACU provides, the memory advisory verbatim
Constraints:
- You MUST state the label plainly; do not soften it to "maybe"
- You MUST cite a concrete dollar figure and percentage when comparing costs
- You SHOULD offer a migration path when the label is
recommended— see migration.md
4. Migration Planning (when applicable)
See migration.md — in-place with a serverless reader, Blue/Green, or snapshot restore.
Constraints:
- You MUST recommend testing on a snapshot-restored cluster before production
- You MUST mention that
innodb_buffer_pool_sizeis auto-managed (resized with ACU) so don't hard-code it, and thatmax_connectionsis derived from the cluster's maximum ACU (static; reboot to change), not current capacity - ACU scaling (min/max changes) is non-disruptive and allowed after user confirmation. Deletion is blocked — see SKILL.md Safety guidance.
- You SHOULD offer a CloudFormation or CDK snippet when the user's stack is IaC-managed
Troubleshooting
Calculator reports "exceeds capacity". Projected peak ACU > 256; Aurora serverless cannot service this cluster. Recommend staying on provisioned or splitting across multiple serverless clusters.
Live pricing fetch fails with ExpiredToken. Refresh credentials (aws sts get-caller-identity) or rerun with --offline for static us-east-1 pricing.
Calculator returns $0 compute for the provisioned comparison. Instance type missing from the static catalog. Run list-instances.
Scale-to-zero questions. Incompatible with RDS Proxy, binary logging (binlog) enabled, Global Database primary, Zero-ETL. See concepts.md for the full list and supported versions.
256 ACU + HA failover. Aurora has exactly one writer per cluster; readers can be serverless or provisioned. Two writers is not valid.
Deep-Dive References
- formulas-and-examples.md — Inline ACU sizing/pricing formulas and pricing tables. Use when you can't run
scripts/acu_calculator.py. - worked-examples.md — Worked examples (basic sizing; migration with CFN/CDK snippets) and scale-to-zero/auto-pause rules.
- concepts.md — ACU fundamentals, scaling, scale-to-zero requirements, pricing
- migration.md — Migration approaches, parameter group rules, CFN/CDK examples
Aurora serverless — Migration & Configuration
This file advises on migration approaches — it never supplies runnable mutation commands. The skill is assessment-only. Mutation actions belong in the customer's change-control process; this file describes the console paths and flag names so the customer (or their IaC stack) can execute them safely.
Migration Approaches (three options)
1. In-Place Modification (minimal downtime)
Add an Aurora serverless reader, test under production traffic, failover to promote it, remove old instances. Steps, with their console path or flag name (never a runnable command):
1. Add a serverless reader. RDS console → Databases → your cluster → Actions → Add reader. Set the instance class to db.serverless. The underlying API is create-db-instance with --db-instance-class db.serverless, but run it through your IaC / change-control tool, not ad-hoc. 2. Set scaling configuration. RDS console → your cluster → Modify → Aurora serverless scaling configuration → set MinCapacity and MaxCapacity (typically 2 and your expected peak ACU). The underlying API is modify-db-cluster with --serverless-v2-scaling-configuration MinCapacity=N,MaxCapacity=M. 3. Failover to promote the serverless reader. RDS console → your cluster → Actions → Failover, choosing the serverless reader as the target. The underlying API is failover-db-cluster with --target-db-instance-identifier. 4. Remove the old provisioned instance. RDS console → your cluster → the old instance → Actions → Delete. The underlying API is delete-db-instance.
Testing window. Observe the serverless reader under production traffic for at least 24 hours before the failover. Monitor ServerlessDatabaseCapacity in CloudWatch to confirm ACU actually scales up under load.
2. Blue/Green Deployment (recommended for production)
Create a Blue/Green deployment. The green environment is a new cluster (pointed at a new Aurora serverless writer) built as a replica of blue. Test green under mirrored load, then switchover. Rollback is trivial — the blue environment is still intact until you explicitly delete it.
Console path: RDS console → Databases → your cluster → Actions → Create blue/green deployment. API endpoint (for reference, not to run ad-hoc): create-blue-green-deployment. Switchover API endpoint: switchover-blue-green-deployment. Both belong in your change-control workflow.
3. Snapshot Restore (cutover window, highest isolation)
Snapshot the provisioned cluster, restore to a new Aurora serverless cluster, validate end-to-end, then cutover application connections. Highest isolation and test fidelity; requires a maintenance window because the application cuts between two clusters.
Console path: RDS console → your cluster → Actions → Take snapshot → (wait) → Actions → Restore snapshot → set writer instance class to db.serverless. API endpoints: create-db-cluster-snapshot, restore-db-cluster-from-snapshot.
Parameter Group Considerations (critical for Aurora serverless)
- Aurora serverless uses the same parameter-group families as provisioned (for Aurora MySQL,
aurora-mysql8.0). - During scaling, Aurora serverless dynamically resizes a small set of memory-sizing parameters and IGNORES any custom values you set:
innodb_buffer_pool_size,innodb_purge_threads,table_definition_cache,table_open_cache. Remove explicit overrides of these before migrating — they will be ignored by the auto-scaling mechanism. max_connectionsdoes NOT scale up/down with ACU — Aurora holds it CONSTANT, derived from the MAXIMUM ACU (not current capacity), as a static parameter that requires a reboot to change. You may still customize it via a formula in a custom parameter group; if you do, prefer a formula tied to capacity rather than a fixed constant.- Before migrating, remove explicit overrides of
innodb_buffer_pool_size,innodb_buffer_pool_instances, andinnodb_log_file_size— Aurora serverless manages buffer-pool sizing with ACU. - Custom parameters for logging, auth, or specific behavior can stay — they don't interact with ACU scaling.
CloudFormation snippet (for IaC migration)
Resources:
ClusterParameterGroup:
Type: AWS::RDS::DBClusterParameterGroup
Properties:
Family: aurora-mysql8.0
Description: Enforce TLS for Aurora serverless cluster
Parameters:
require_secure_transport: "ON"
AuroraCluster:
Type: AWS::RDS::DBCluster
Properties:
Engine: aurora-mysql
EngineVersion: "8.0.mysql_aurora.3.08.0"
DBClusterParameterGroupName: !Ref ClusterParameterGroup
ServerlessV2ScalingConfiguration:
MinCapacity: 2
MaxCapacity: 64
StorageEncrypted: true
EnableCloudwatchLogsExports:
- error
- slowquery
- audit
WriterInstance:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceClass: db.serverless
Engine: aurora-mysql
DBClusterIdentifier: !Ref AuroraClusterThe custom cluster parameter group enforces require_secure_transport=ON (the Aurora MySQL equivalent of PostgreSQL's rds.force_ssl=1). The Aurora MySQL default.* parameter groups ship with require_secure_transport=OFF, so a migration that reuses the default would silently drop the in-transit TLS requirement the skill mandates for production.
This is a definition of Aurora serverless infrastructure for your IaC stack (CloudFormation, Terraform, or CDK). Deploy it through your normal change-control process — this skill does not run CloudFormation for you.
CDK (TypeScript) snippet
const parameterGroup = new rds.ParameterGroup(this, 'PG', {
engine: rds.DatabaseClusterEngine.auroraMysql({
version: rds.AuroraMysqlEngineVersion.VER_3_08_0,
}),
parameters: { 'require_secure_transport': 'ON' },
});
const cluster = new rds.DatabaseCluster(this, 'Cluster', {
engine: rds.DatabaseClusterEngine.auroraMysql({
version: rds.AuroraMysqlEngineVersion.VER_3_08_0,
}),
parameterGroup,
serverlessV2MinCapacity: 2,
serverlessV2MaxCapacity: 64,
writer: rds.ClusterInstance.serverlessV2('writer'),
readers: [
rds.ClusterInstance.serverlessV2('reader', {
scaleWithWriter: true,
}),
],
storageEncrypted: true,
cloudwatchLogsExports: ['error', 'slowquery', 'audit'],
});This skill describes what to do and where to do it. It does not emit copy-pasteable mutation commands. If you need the exact CLI for a migration step, copy the API endpoint name from this file (e.g., modify-db-cluster) and build the command yourself from the AWS CLI reference — or use the console path described above. That keeps change-control in your team's hands, which is where it belongs.
Aurora serverless — Worked Examples and Scale-to-Zero Rules
Companion to instructions.md and formulas-and-examples.md. The formulas and pricing tables used to produce these numbers live in formulas-and-examples.md.
Worked example — basic sizing
User asks: db.r6g.xlarge, CPU P95 35%, max 72%, 500 GiB storage, should I switch to Aurora serverless? Region: us-east-1.
Agent reply should follow this pattern — present the script output, no derivation shown:
"Loading references/serverless-advisory-instructions.md — this is the serverless sizing workflow.>
The command to run this analysis yourself from a machine with Python 3.10+ and boto3 installed:
>
```bash
python3 scripts/acu_calculator.py estimate \
--instance db.r6g.xlarge --cpu-p95 35 --cpu-max 72 --storage 500 --region us-east-1
```
>
Running that with your parameters produces:
>
| Metric | Value |
|---|---|
| min ACU | 3.5 |
| max ACU | 16.0 |
| typical ACU | 6.0 |
| Peak ACU vs 256 ceiling | Well under (12.0/256) |
>
| Monthly cost | Amount |
|---|---|
| Provisioned db.r6g.xlarge compute | $379 |
| Aurora serverless at typical ACU compute | $526 |
| Aurora serverless total range (min–max, incl. storage) | $357–$1,452 |
| Storage (500 GiB × $0.10) | $50 |
| Total Provisioned / Total Aurora serverless (typical) | $429 / $576 |
>
Recommendation: not_recommended. At typical load, Aurora serverless is ~34% more expensive than provisioned ($576 vs $429). Your workload (P95 35%, max 72%) is steady — the burst/idle pattern that makes serverless win isn't present. Stay on db.r6g.xlarge. If you want the savings anyway, consider a 1-year RI on the provisioned instance (~30% off compute), which brings total to ~$315/mo."
Note what this reply does NOT include: no "0.21 × 4 × 4 = 3.36, round up to 3.5" arithmetic; no formula derivation; no "here's how I computed it." Just the script-style output.
(The internal formulas for producing these numbers are in formulas-and-examples.md. Use them to generate the output, but don't copy them into the reply.)
Worked example — migration with connection and working-set constraints
User: db.r7g.2xlarge, 200 peak connections, 12 GiB working set, P95 40%, max 65%, 1 TB storage, region eu-west-1. Walk through the whole thing.
Agent response pattern (abridged):
1. Announce reference: "Loading references/serverless-advisory-instructions.md." 2. Print:
python3 scripts/acu_calculator.py estimate \
--region eu-west-1 \
--instance db.r7g.2xlarge \
--cpu-p95 40 --cpu-max 65 \
--connections 200 --working-set 12 --storage 1000 \
--num-instances 1 --format table3. Script output (db.r7g.2xlarge, 8 vCPU): min_ACU 8.0 (working-set floor 12/2 = 6.0 is satisfied), peak_ACU 21.0, max_ACU 27.5 (= max(round_up(21.0 × 1.30), round_up(13.5 × 1.50))), typical_ACU 13.5. 4. Costs: offline/static mode prices at us-east-1 rates ($0.12/ACU-hr, $0.10/GiB-mo storage) regardless of --region; only live API mode applies the eu-west-1 rate. Aurora serverless typical 13.5 × $0.12 × 730 = $1,183/mo compute, storage 1000 × $0.10 = $100, total$1,283/mo. Provisioned r7g.2xlarge: $1.106 × 730 = $807 + $100 = $907/mo. Recommendation: not_recommended — serverless is ~41% more at this steady load. 5. Memory advisory: your 12 GiB working set means min_ACU MUST be ≥ 6.0 to keep the hot set in memory. min_ACU=8 already satisfies this. If you set min below 6 you will see buffer-cache misses. 6. Migration approaches:
- In-place (
modify-db-cluster --serverless-v2-scaling-configuration): simplest, fastest; brief restart per instance. Caveats: cluster must already be r6+ generation. - Blue/Green (recommended for production): new Aurora serverless writer built as green environment, tested under live replication, switchover in <1 min. Best rollback path.
- Snapshot restore to a new cluster: full rebuild; longest, but gives you a parallel cluster to test end-to-end.
7. Parameter group: MUST be an Aurora serverless-compatible parameter group (family aurora-mysql8.0). DO NOT hardcode innodb_buffer_pool_size (Aurora resizes it with ACU) or max_connections (Aurora derives it from the cluster's maximum ACU; it is static and needs a reboot to change). Override only for well-understood special cases. 8. CloudFormation snippet (Aurora serverless writer):
ClusterParameterGroup:
Type: AWS::RDS::DBClusterParameterGroup
Properties:
Family: aurora-mysql8.0
Description: Enforce TLS for Aurora serverless cluster
Parameters:
require_secure_transport: "ON"
DBCluster:
Type: AWS::RDS::DBCluster
Properties:
Engine: aurora-mysql
EngineVersion: "8.0.mysql_aurora.3.08.0"
DBClusterParameterGroupName: !Ref ClusterParameterGroup
ServerlessV2ScalingConfiguration:
MinCapacity: 8.0
MaxCapacity: 27.5
StorageEncrypted: true
EnableCloudwatchLogsExports:
- error
- slowquery
- audit
WriterInstance:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceClass: db.serverless
Engine: aurora-mysql
DBClusterIdentifier: !Ref DBCluster9. CDK snippet (TypeScript):
const parameterGroup = new rds.ParameterGroup(this, "PG", {
engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_3_08_0 }),
parameters: { "require_secure_transport": "ON" },
});
const cluster = new rds.DatabaseCluster(this, "Cluster", {
engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_3_08_0 }),
parameterGroup,
serverlessV2MinCapacity: 8.0,
serverlessV2MaxCapacity: 27.5,
writer: rds.ClusterInstance.serverlessV2("Writer"),
storageEncrypted: true,
cloudwatchLogsExports: ["error", "slowquery", "audit"],
});10. Testing: snapshot-restore to a test cluster first; run full load tests at peak TPS; observe ServerlessDatabaseCapacity CloudWatch metric to verify ACU actually scales with load.
Scale-to-zero / auto-pause rules
Aurora serverless auto-pause requires MinCapacity: 0 and is incompatible with: RDS Proxy, binary logging (binlog) enabled, Global Database primary, and Zero-ETL integrations. If the user's workload has any of these, you MUST warn them that scale-to-zero cannot be enabled, and instead recommend a non-zero MinCapacity (e.g. 0.5 for dev/test, ≥1.0 for prod). In a multi-AZ cluster, auto-pause still works: the writer and any reader instances with failover priority 0 or 1 pause and resume together (their capacity is tied to the writer), while reader instances with failover priority 2-15 can pause independently. So a reader configured with priority 0/1 will not pause unless the writer also pauses — but the cluster as a whole can still scale to zero. See concepts.md for the complete compatibility matrix.
Security Considerations
The amazon-aurora skill creates and modifies Aurora resources when the user requests it, but blocks destructive operations (deletes, major upgrades, purchases). The agent MUST enforce the practices below.
Table of Contents
1. IAM Principles 2. Credential Hygiene 3. RDS Data API Warning 4. Secure Defaults in Examples 5. Output Handling
IAM Principles
The caller's IAM principal needs read and write permissions for RDS to create and modify clusters. Scope permissions to the minimum required actions.
Required permissions (by service):
| Service | Required actions |
|---|---|
| RDS | rds:DescribeDBClusters, rds:DescribeDBInstances, rds:DescribeDBEngineVersions, rds:DescribeReservedDBInstancesOfferings |
| CloudWatch | cloudwatch:GetMetricStatistics, cloudwatch:ListMetrics |
| Pricing | pricing:GetProducts, pricing:DescribeServices |
| Savings Plans | savingsplans:DescribeSavingsPlansOfferings, savingsplans:DescribeSavingsPlansOfferingRates |
Managed policies AmazonRDSReadOnlyAccess and CloudWatchReadOnlyAccess cover most of this; add Pricing and Savings Plans read actions via a scoped custom policy.
Do NOT use AdministratorAccess or *:FullAccess managed policies. Scope write permissions to the specific actions the skill uses: rds:CreateDBCluster, rds:CreateDBInstance, rds:ModifyDBCluster, rds:ModifyDBInstance, rds:AddTagsToResource, rds:RemoveTagsFromResource. For reads: rds:Describe*, rds:List*.
Credential Hygiene
- Prefer short-lived credentials (IAM roles,
ada credentials update, SSO) over long-lived IAM user keys. - Do NOT create or store long-lived DB passwords from within the skill. If the user's Isengard credentials are expired, prompt them to refresh outside the skill.
- IAM auth tokens are approved. Calling
aws rds generate-db-auth-tokenorrds_client.generate_db_auth_token()is explicitly safe — these produce short-lived (15-minute) tokens derived from the caller's IAM identity. They are not stored credentials. Use them when IAM database authentication is enabled on the cluster. - Do NOT log or echo DB passwords or raw secret values. For RDS Data API precheck runs, reference secrets by their
secretArnand let the service resolve them. - For SSM Run Command prechecks, pass DB credentials via inline JSON parameters attached to the Run Command invocation — never via positional filesystem arguments.
RDS Data API Warning
Enabling RDS Data API solely to run upgrade prechecks widens the cluster's connectivity surface. The Data API endpoint is HTTPS-reachable over the public AWS plane (authenticated with IAM), so it's safer than opening a new SG ingress rule, but it's still an additional attack surface.
- Warn the user before recommending they enable Data API for a one-off precheck run
- If the cluster is production and Data API is not already enabled, prefer the
user-runs-scriptprecheck method instead - If Data API is enabled for the workflow, remind the user to disable it after prechecks if it wasn't previously in use
Secure Defaults in Examples
Any CloudFormation, CDK, or AWS CLI snippet produced by this skill MUST use secure defaults:
- Cluster configuration:
StorageEncrypted: true(andKmsKeyIdif the user has a customer-managed key) - TLS: cluster parameter group enables
require_secure_transport=ON - Security groups: scoped CIDR ranges or security-group references — NEVER
0.0.0.0/0or::/0 - Public accessibility: NEVER use
--publicly-accessible. If the user needs connectivity from outside the VPC, use the RDS Data API (HTTPS + IAM), an EC2 bastion with SSH tunnel, or VPN/Direct Connect into the VPC. - Parameter groups: do NOT disable
general_log,slow_query_log, the audit log (server_audit_logging), or other audit/logging parameters "for convenience" - Logging & monitoring: recommend enabling CloudTrail so Aurora control-plane API activity (create / modify / delete / failover) is recorded, and CloudWatch alarms on security-relevant metrics such as
LoginFailuresandDatabaseConnections. CloudWatch log exports (error,slowquery,audit) give query-level visibility but do not cover API-level activity — CloudTrail does. - Resource names: no
prod,production, orPRODas example/default values — those get copy-pasted into production accidentally
Output Handling
- Cost numbers, instance types, and cluster IDs are not sensitive on their own, but combined with account ID they reveal environment topology. When presenting results, don't unnecessarily include the account ID.
- If a workflow surfaces a secret ARN, show only the ARN, never attempt to resolve it.
- Upgrade precheck findings may include schema names, table names, or query text from the user's database. If the output is going to be shared (posted to a ticket, shared in chat), warn the user to review for sensitive identifiers before sharing.
References
Aurora MySQL Upgrade Documentation Links
Version Information
- Aurora MySQL LTS: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Update.SpecialVersions.html
- Aurora MySQL Release Notes: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraMySQLReleaseNotes/AuroraMySQL.Updates.30Updates.html
- Aurora MySQL Release Calendar: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraMySQLReleaseNotes/AuroraMySQL.release-calendars.html
Aurora MySQL Upgrade Blogs
- Upgrade to Aurora MySQL version 3: https://aws.amazon.com/blogs/database/upgrade-to-amazon-aurora-mysql-version-3-with-mysql-8-0-compatibility/
- v2 to v3 upgrade checklist Part 1: https://aws.amazon.com/blogs/database/amazon-aurora-mysql-version-2-with-mysql-5-7-compatibility-to-version-3-with-mysql-8-0-compatibility-upgrade-checklist-part-1/
- v2 to v3 upgrade checklist Part 2: https://aws.amazon.com/blogs/database/amazon-aurora-mysql-version-2-with-mysql-5-7-compatibility-to-version-3-with-mysql-8-0-compatibility-upgrade-checklist-part-2/
- Major version upgrades with minimum downtime: https://aws.amazon.com/blogs/database/performing-major-version-upgrades-for-amazon-aurora-mysql-with-minimum-downtime/
Aurora MySQL AWS Documentation
- Upgrade prechecks: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.upgrade-prechecks.html
- Precheck descriptions: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.upgrade-prechecks.descriptions.html
- Blue/Green Deployments: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/blue-green-deployments.html
Oracle MySQL 8.0 Documentation (behavior changes)
- What's New in MySQL 8.0: https://dev.mysql.com/doc/refman/8.0/en/mysql-nutshell.html
- MySQL 8.0 Release Notes: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/
- Upgrading to MySQL 8.0: https://dev.mysql.com/doc/refman/8.0/en/upgrading.html
- MySQL 8.0 Reserved Keywords: https://dev.mysql.com/doc/refman/8.0/en/keywords.html
- MySQL 8.0 Upgrade Best Practices: https://dev.mysql.com/doc/refman/8.0/en/upgrade-best-practices.html
Aurora Upgrade Advisor Workflow
Guide customers through Aurora MySQL major and minor version upgrades. Identifies the cluster, recommends target versions (latest vs LTS), runs live prechecks, flags query-plan regressions, and surfaces pre- and post-upgrade checklists. Major version upgrades are blocked — see SKILL.md Safety guidance. This reference helps plan the upgrade.
Execute commands using available tools from the AWS MCP server when connected (sandboxed execution, audit logging, observability). Fall back to the AWS CLI or shell when the MCP server is not available.
When This Applies
User mentions: upgrade Aurora cluster, what version should I upgrade to, pre-upgrade checklist, post-upgrade steps, Aurora LTS, upgrade prechecks, Aurora MySQL upgrade, or major/minor version upgrade.
Two response modes
Mode A — Advisory (no cluster named): User asks a general question like "what version should I upgrade to?" or "what's the LTS version?" without specifying a cluster. Skip directly to LTS recommendation (see "Mode A workflow" below). Do NOT ask for cluster ID and region first — recommend the LTS version with rationale, then offer to run the live workflow if they want a cluster-specific assessment.
Mode B — Cluster-specific (cluster named): User names a cluster identifier or asks you to plan an upgrade for a specific cluster. Run the full workflow (Tasks 1–8) with live AWS calls. Tasks are split across:
- mode-b-discovery.md — Tasks 1–4: permissions, no-fabrication guard, acquire parameters, identify the cluster, determine upgrade targets.
- lts-recommendation.md — Task 5: recommend two options (LTS vs latest), with the authoritative current-LTS table and trade-offs.
- mode-b-prechecks-checklists.md — Tasks 6–8: live database prechecks, query-load analysis, pre/post-upgrade checklists and engine-specific blockers.
When the user reports a completed upgrade and asks what to check now, route to post-upgrade-validation.md (must-surface Aurora items + immediate cluster-state checks) and post-upgrade-detail.md (statistics refresh, extension updates, plan verification, parameter-group-family migration, snapshot rollback window, monitoring window).
Mode A workflow (advisory, ~3-4 paragraphs)
When the user asks "what version should I upgrade to?" with their current version (e.g., "I'm on 3.04") but no cluster ID:
1. Lead with LTS recommendation. State the designated Aurora MySQL LTS version (Aurora MySQL 3.10 LTS) and frame it as the recommended target. Be direct — don't ask for more info first. 2. Explain LTS rationale: longer support window (~3 years of critical fixes), fewer forced upgrade cycles, suitable when stability matters more than new features. 3. Mention the latest non-LTS option as the alternative for users wanting newer features, but make clear LTS is the default recommendation. 4. State the upgrade path: for major version jumps from old versions (e.g., Aurora MySQL 2.x → 3.x, i.e. MySQL 5.7 → 8.0-compatible), the upgrade may require an intermediate hop or Blue/Green deployment. Direct major-version upgrades require prechecks, a maintenance window, and a rollback plan. 5. Offer the cluster-specific workflow as a follow-up: "If you share your cluster ID and region, I can pull the exact valid upgrade targets, run prechecks, and produce a pre/post-upgrade checklist."
Mode A does NOT need cluster identifier, region, or live AWS calls. It is general guidance, version-independent. For the authoritative current-LTS table and the LTS/latest trade-offs, see lts-recommendation.md.
Troubleshooting
Cluster not found. Check region and cluster identifier. For Global Databases, use describe-global-clusters with the global cluster identifier.
Engine version shows `-limitless`. Not applicable to Aurora MySQL — Aurora Limitless is an Aurora PostgreSQL-only capability. If you are actually working with an Aurora PostgreSQL cluster, use the amazon-aurora-postgresql skill.
Precheck queries time out via SSM. Increase the SSM timeout, or switch to RDS Data API if enabled. Large schemas can take minutes for information_schema queries.
RDS Proxy compatibility unclear. Check target version release notes. If unclear, test on a snapshot-restored clone with the proxy attached before production.
User wants to roll back after a successful upgrade. Rollback requires snapshot restore — no in-place downgrade. If the cluster is functioning but has a regression, debug it rather than roll back. See the post-upgrade checklist for regression-hunting steps.
Deep-Dive References
- mode-b-discovery.md, lts-recommendation.md, mode-b-prechecks-checklists.md — the Mode B cluster-specific workflow (Tasks 1–8)
- post-upgrade-validation.md, post-upgrade-detail.md — post-upgrade validation must-surface items and detailed procedures
- prechecks-mysql.md — live precheck SQL
- query-load-mysql.md — regression detection via EXPLAIN
- pre-checklist.md, post-checklist.md — actionable checklists
- documentation-links.md — authoritative AWS documentation pointers
Recommend Two Options — LTS vs Latest (Task 5)
Part of the Mode B workflow (see mode-b-discovery.md for Tasks 1–4). Also used by Mode A advisory answers.
Always present both:
- Latest version: highest minor within the newest supported major. More features and performance, but shorter support window before the next required upgrade.
- LTS version: Aurora MySQL 3.10 (a designated LTS minor). Extended support window (~3 years), critical fixes only, fewer required upgrade cycles.
Designated LTS versions
This table is authoritative for "what's the LTS version right now" questions when live AWS is unreachable. AWS designates an LTS release per supported major version simultaneously — there is no single engine-wide LTS value. The correct LTS minor is whatever describe-db-engine-versions / the AWS LTS page lists for the major the customer targets. You MUST answer "what's the current LTS version" per the major the customer is on (the LTS minor depends on the major), not as a single engine-wide answer, and you MUST NOT list every minor version as if each were LTS. These move over time — verify via describe-db-engine-versions.
| Engine | Designated LTS (one per major) | Non-LTS (also supported) | Aurora's LTS commitment |
|---|---|---|---|
| Aurora MySQL | 3.10.\ and 3.04.\** (designated LTS minors) | 8.4.x (latest major, compatible with community MySQL 8.4 LTS — verify available minors via describe-db-engine-versions), 3.11+/3.12 (MySQL 8.0-compatible, prior major; non-LTS) | Minimum ~3 years of Aurora-extended support on each LTS minor, with only critical / security patches. |
Important clarifications (address these explicitly when the user asks about LTS):
1. LTS is not a separate MAJOR version. It's a specific MINOR release within a supported major that Aurora designates as "Long-Term Support." For Aurora MySQL, 3.10 is an LTS minor within the version-3 (MySQL-8.0-compatible) major; other 3.y minors are released on the regular cadence but the designated LTS minor receives only critical fixes and is supported for ~3 years. AWS designates one LTS minor per supported major simultaneously (3.10 and 3.04 are both designated LTS minors). 2. Older versions are NOT LTS just because they're older — but a major can have more than one designated LTS minor. Do not treat any old minor as LTS. For Aurora MySQL the designated LTS minors are 3.10 and 3.04; other 3.x minors (e.g. 3.11/3.12) are non-LTS, and the real lifecycle caveat for older minors is end-of-standard-support, not LTS status. 3. LTS is opt-in via parameter choice at upgrade time — you're not automatically on LTS. When upgrading, you choose an LTS minor (e.g. 3.10) vs. a latest non-LTS minor. 4. Why pick LTS over latest:
- Longer stability window: ~3 years of support vs. ~1 year for a non-LTS minor.
- Patch cadence is predictable: only critical fixes land; no quarterly feature-or-behaviour changes that force re-testing.
- Fewer required upgrade cycles: reduce operational overhead for teams that can't test upgrades quarterly.
- Regulatory alignment: auditors often expect 1–3 year rolling platform refresh cycles; LTS fits cleanly.
5. Why pick latest over LTS:
- Access to new features: window functions and CTEs (MySQL 8.0), instant DDL improvements, JSON enhancements, newer SQL syntax, better optimizer/parallelism.
- Performance improvements: newer versions are typically 5–15% faster on analytic workloads.
- Security modernization: deprecated crypto removed sooner.
6. Trade-offs you MUST surface when the user asks:
- On LTS you must disable automatic minor version upgrades, or Aurora will move you off the LTS minor onto the latest non-LTS during the next maintenance window. Set
AutoMinorVersionUpgrade: falseon the cluster and its instances. - Staying on LTS means you will not get non-critical bug fixes or new features until you deliberately upgrade to a later LTS or the current latest.
- LTS versions also get upgraded eventually — when Aurora designates a new LTS minor for a newer major (the current LTS minors change over time; verify via
describe-db-engine-versions), you'll need a major-version upgrade cycle then too.
Constraints:
- You MUST present both options with trade-offs, not just one
- You MUST NOT recommend LTS unconditionally — frame it as a choice based on risk tolerance and upgrade-cadence capacity
- You MUST NOT list every minor version as if it were LTS. AWS designates one LTS minor per supported major, so the answer depends on the major the customer targets.
- When the user asks "what's the LTS version right now", you MUST cite the table above and the specific LTS minor for the relevant major (e.g. "the current Aurora MySQL LTS minors are 3.10 and 3.04"), not a long list of minor versions.
Post-Upgrade Checklist
Common Steps
1. Verify upgrade completed
aws rds describe-db-clusters --db-cluster-identifier {cluster} \
--query "DBClusters[0].{Engine:Engine,EngineVersion:EngineVersion,Status:Status}" \
--output json --region {region}2. Preserve the rollback window — do NOT delete pre-upgrade snapshots immediately. Major version upgrades are one-way in-place. Rollback requires restoring from a snapshot or PITR, and both restore the old major version:
- Any pre-upgrade manual snapshot restores to the engine version it was taken on (e.g., an Aurora MySQL 3.04 snapshot restores to 3.04 — not to a post-upgrade 3.10).
- PITR to any time before the upgrade completed restores the pre-upgrade major version, not the new one.
- After the upgrade, Aurora cannot restore backward-in-time into the new major version; that timeline starts at the upgrade's completion.
Keep the pre-upgrade manual snapshot for at least 7–14 days of stable production traffic (longer for regulated workloads) before deleting it. Deleting it early forecloses the cheapest rollback path. Document the snapshot identifier and retain-until date in your change record.
3. Check performance discrepancies — Compare CloudWatch metrics against baseline: CPUUtilization, DatabaseConnections, ReadLatency, WriteLatency, FreeableMemory, BufferCacheHitRatio, DMLLatency, SelectLatency. Use Performance Insights to compare database load.
4. Compare EXPLAIN plans for critical queries. Look for: different join strategies, missing index usage, full table scans.
EXPLAIN FORMAT=JSON SELECT ...;(orEXPLAIN ANALYZEon MySQL 8.0+ for actual row counts)
5. Monitor CloudWatch 24-72 hours — Watch: CPUUtilization, FreeableMemory, DatabaseConnections, ReadLatency, WriteLatency, AuroraReplicaLag, Deadlocks, LoginFailures.
6. Validate application connectivity — connections, pooling, SSL/TLS.
7. Verify parameter group applied correctly:
aws rds describe-db-cluster-parameters --db-cluster-parameter-group-name {new_pg} \
--query "Parameters[?Source=='user'].{Name:ParameterName,Value:ParameterValue}" \
--output table --region {region}8. Update statistics — run ANALYZE TABLE on hot tables to rebuild optimizer statistics for the new version.
9. Check error logs
aws rds describe-events --source-identifier {cluster} --source-type db-cluster --duration 1440 --region {region}Aurora MySQL-Specific
1. Verify auth plugin compatibility — SELECT user, host, plugin FROM mysql.user; Check if apps need mysql_native_password.
2. Check GROUP BY sorting — 8.0 no longer implicitly sorts. Apps relying on this need explicit ORDER BY.
3. Validate stored procedures — run critical routines, check for deprecated syntax.
4. Verify query cache removal impact — if query cache was enabled, monitor for increased CPU/latency. Consider ElastiCache if hit ratio was high.
Related skills
FAQ
Does Aurora MySQL support express configuration?
No. Express configuration is PostgreSQL-only; Aurora MySQL uses full VPC-based configuration.
Does it require confirmation before creating a cluster?
Yes. It must confirm with the user before any create or modify operation and applies resource tags on creation.