Amazon RDS for MySQL - The Complete Production Guide (2026)
Running MySQL yourself is easy at the beginning. Running it reliably in production is a completely different challenge. You must plan for backups, failover, monitoring, upgrades, connection spikes, security, scaling, and disaster recovery—all while keeping the application available.
Amazon Relational Database Service (Amazon RDS) removes much of this operational burden. AWS manages infrastructure provisioning, database software installation, automated backups, patching, and many common recovery operations, while you remain responsible for database design, access control, performance tuning, cost, and application behaviour.
The commands and naming are adapted into one independent, production-aware tutorial that you can follow in your own AWS account.
What You Will Learn
By the end of this guide, you will understand how to:
- Create an encrypted RDS for MySQL database in private subnets
- Retrieve credentials safely from AWS Secrets Manager
- Connect to and verify the database without exposing it publicly
- Configure Multi-AZ high availability and test failover
- Monitor database load with CloudWatch Database Insights
- Generate load and identify expensive SQL statements
- Scale compute and storage safely
- perform minor and major MySQL upgrades using safer deployment patterns
- Create snapshots and perform point-in-time recovery
- Manage database configuration with parameter groups
- Export and analyse snapshot data with AWS Glue and Amazon Athena
- Protect applications from connection storms with RDS Proxy
- Apply production security controls
- Design cross-Region disaster recovery
- Understand when active-active replication is appropriate
- Clean up every chargeable resource created during the tutorial
Prerequisites
You will need:
- An AWS account with permission to manage RDS, EC2, VPC, IAM, KMS, Secrets Manager, CloudWatch, S3, Glue, and Athena
- AWS CLI v2 configured with an IAM identity—not long-lived root credentials
- A VPC spanning at least two Availability Zones
- Two private database subnets
- A private EC2 administration host, CloudShell VPC environment, or another secure host that can reach the database
- The MySQL 8 client
- Basic knowledge of SQL and AWS networking
This guide uses eu-west-2. Replace the Region, subnet IDs, security-group IDs, account IDs, passwords, and resource names with your own values.
Cost warning: RDS instances, Multi-AZ deployments, RDS Proxy, NAT gateways, cross-Region resources, Advanced Database Insights, snapshots, Glue, Athena, and data transfer can incur charges. Complete the cleanup section when you finish.
Architecture Overview
The main production architecture contains:
- An application or administration host in private application subnets
- An RDS Proxy endpoint for connection pooling
- An encrypted RDS for MySQL primary database in private database subnets
- A synchronous standby in a second Availability Zone when Multi-AZ is enabled
- Secrets Manager for database credentials
- CloudWatch for metrics, logs, alarms, and Database Insights
- S3, Glue, and Athena for offline analytics
- A cross-Region replica or copied snapshot for disaster recovery
The database should not have a public IP address. Network access should flow from a narrowly scoped application security group to the database or proxy security group on TCP port 3306.
Configure the Working Environment
Set reusable environment variables:
1 | export AWS_REGION="eu-west-2" |
Create a DB subnet group using at least two private subnets in different Availability Zones:
1 | aws rds create-db-subnet-group \ |
For production, keep the following network rules:
- RDS and the proxy must be deployed in private subnets.
- Set Publicly accessible to No.
- The DB security group should accept
3306only from the proxy security group, or directly from the application security group when no proxy is used. - Do not allow
0.0.0.0/0or::/0to the MySQL port. - Use Systems Manager Session Manager instead of opening SSH to the internet.
Create an Encrypted RDS for MySQL Instance
The simplest production starting point is a single RDS DB instance with encryption, backups, deletion protection, log exports, and Secrets Manager-managed credentials. Multi-AZ will be enabled in the next section.
1 | aws rds create-db-instance \ |
AWS can manage and rotate the master password through Secrets Manager. This prevents the password from appearing in shell history, CloudFormation parameters, CI/CD logs, or source code. RDS supports this integration for DB instances and Multi-AZ DB clusters, although some features have compatibility limitations.
Wait until the instance becomes available:
1 | aws rds wait db-instance-available \ |
Retrieve the endpoint and secret ARN:
1 | aws rds describe-db-instances \ |
RDS endpoints are DNS names. Applications must resolve the endpoint on every new connection and must not pin its current IP address.
Connect Securely and Verify MySQL
From a host that can reach the private DB subnets, retrieve the secret without printing it into application logs:
1 | export SECRET_ARN=$(aws rds describe-db-instances \ |
Run a small verification inside MySQL:
1 | SELECT VERSION(), @@hostname, @@port, NOW(); |
If the connection fails, verify DNS resolution, route tables, network ACLs, security-group references, the DB status, port 3306, and whether your client requires the current AWS RDS certificate bundle.
Configure Multi-AZ High Availability
A standard Multi-AZ DB instance keeps a synchronous standby in another Availability Zone. The standby is maintained for failover; it is not a read-scaling endpoint. For read scaling, create read replicas or consider a Multi-AZ DB cluster where supported.
Enable Multi-AZ:
1 | aws rds modify-db-instance \ |
Verify it:
1 | aws rds describe-db-instances \ |
Test a controlled failover during a maintenance window:
1 | aws rds reboot-db-instance \ |
Your application must use short connection timeouts, bounded retries with exponential backoff and jitter, and a pool that discards broken connections. The RDS DNS endpoint remains the logical connection target, but existing TCP sessions do not survive failover.
Monitor with CloudWatch Database Insights
CPU alone does not explain database performance. A database can be slow because of locks, I/O waits, too many connections, inefficient indexes, temporary tables, or a few expensive statements.
CloudWatch Database Insights gives a fleet-level view of database health and uses Performance Insights data to help analyse database load. In the console, open:
CloudWatch → Insights → Database Insights → select the RDS instance
Pay attention to:
- DB load / Average Active Sessions (AAS) compared with available vCPUs
- Top SQL to identify expensive statement fingerprints
- Top waits to distinguish CPU, I/O, locks, and concurrency pressure
CPUUtilizationDatabaseConnectionsFreeableMemoryFreeStorageSpaceReadLatencyandWriteLatencyReadIOPSandWriteIOPSDiskQueueDepthReplicaLagfor read replicas
Create a basic CPU alarm:
1 | aws cloudwatch put-metric-alarm \ |
In production, send alarms to an SNS topic or incident-management integration and create separate warning and critical thresholds. Also alarm on storage, memory, connections, replica lag, and failover events.
Generate Test Load
sysbench is useful for learning how workload changes appear in Database Insights:
1 | sysbench oltp_read_write \ |
Use Top SQL and wait events to find the bottleneck before resizing the instance. Scaling an inefficient query only makes an expensive problem larger.
Scale and Optimise the Database
Vertical Compute Scaling
Change the DB instance class when CPU, memory, or network capacity is consistently exhausted after query tuning:
1 | aws rds modify-db-instance \ |
Applying immediately can cause an outage. For normal production changes, omit --apply-immediately and let RDS apply the modification during the preferred maintenance window.
Storage Autoscaling
MaxAllocatedStorage allows RDS to increase storage automatically as space is consumed:
1 | aws rds modify-db-instance \ |
Storage autoscaling does not scale storage back down. Set cost alarms and choose a maximum that provides operational headroom without hiding uncontrolled data growth.
Read Scaling
Create a read replica for read-heavy workloads:
1 | aws rds create-db-instance-read-replica \ |
Read replicas use asynchronous replication. Applications must tolerate replica lag and must not send read-after-write operations to a replica when immediate consistency is required.
Handle Minor and Major Upgrades Safely
First discover valid upgrade targets:
1 | CURRENT_VERSION=$(aws rds describe-db-instances \ |
Before any upgrade:
- Read the MySQL and RDS release notes.
- Run compatibility checks against a restored snapshot.
- Test the application, drivers, stored procedures, events, and replication.
- Verify parameter and option-group compatibility.
- Take a manual snapshot.
- Define rollback criteria and an owner for the decision.
- Schedule a change window and monitor application errors and replication lag.
For a minor upgrade:
1 | aws rds modify-db-instance \ |
For major changes, Amazon RDS Blue/Green Deployments can create a synchronized green environment where you test the new engine or parameter configuration before switchover. The green database should normally remain read-only until switchover.
Important: At the time of writing, RDS Blue/Green Deployments do not support a source database whose master password is managed by the RDS integration with Secrets Manager. Always check the latest AWS limitations before choosing the upgrade method.
Back Up and Restore
RDS supports two complementary backup types:
- Automated backups provide point-in-time recovery within the configured retention window.
- Manual snapshots remain until you explicitly delete them and are useful before major changes or for longer retention.
Create a manual snapshot:
1 | export SNAPSHOT_ID="${DB_ID}-$(date +%Y%m%d%H%M)" |
Restore the snapshot into a new DB instance:
1 | aws rds restore-db-instance-from-db-snapshot \ |
For point-in-time recovery, inspect the restorable range:
1 | aws rds describe-db-instances \ |
Then restore:
1 | aws rds restore-db-instance-to-point-in-time \ |
A backup is not proven until you restore it, validate row counts and integrity, and measure the recovery time. Automate restore tests in a non-production account.
Manage Configuration with Parameter Groups
RDS parameter groups are managed collections of MySQL settings. Default groups cannot be edited, so create a custom group that matches your engine family:
1 | aws rds create-db-parameter-group \ |
Attach it:
1 | aws rds modify-db-instance \ |
Some parameters are dynamic; static parameters require a reboot. Check the ApplyType, IsModifiable, and allowed values before modifying anything:
1 | aws rds describe-db-parameters \ |
Treat parameter groups as code. Keep the desired values in Terraform, CloudFormation, or CDK; review changes; and test them against a production-like workload.
Analyse Snapshot Data with S3, Glue, and Athena
RDS can export supported snapshot data to Amazon S3 in Apache Parquet format. This lets analytical tools query a consistent snapshot without sending heavy reporting queries to the production database.
The workflow is:
- Create or reuse an encrypted RDS snapshot.
- Create an S3 bucket and prefix for the export.
- Create an IAM role that RDS can assume to write the export.
- Grant the role access to the destination bucket and KMS key.
- Start the snapshot export task.
- Create a Glue Data Catalog database.
- Run a Glue crawler against the exported Parquet files.
- Query the resulting tables in Athena.
Example export command:
1 | aws rds start-export-task \ |
Monitor it:
1 | aws rds describe-export-tasks \ |
After the Glue crawler creates tables, use Athena for queries such as:
1 | SELECT customer_id, |
This is a batch analytics pattern, not real-time replication. Partition data where possible, compress results, restrict S3 and KMS permissions, and configure an Athena workgroup with query-result encryption and cost controls.
Protect Applications with RDS Proxy
Serverless functions and horizontally scaled services can open thousands of short-lived database connections. MySQL spends memory and CPU managing those sessions. RDS Proxy pools and reuses connections, helps absorb traffic bursts, and can improve application resilience during database failover.
The proxy requires:
- Network access to the DB instance
- A secret for each database user, or supported end-to-end IAM authentication
- An IAM role that allows the proxy to read the required secret and use its KMS key
- TLS enforcement for production clients
Create the proxy:
1 | aws rds create-db-proxy \ |
Retrieve the proxy endpoint:
1 | aws rds describe-db-proxies \ |
Change only the application’s database host to the proxy endpoint. Test transactions carefully: session state, temporary tables, user variables, and some prepared statements can pin a client to one underlying connection and reduce pooling efficiency.
RDS Proxy is useful for connection management; it does not optimise SQL or increase the database’s compute capacity.
Production Security Checklist
Network Security
- Keep RDS private and disable public access.
- Use security-group-to-security-group rules.
- Separate application, proxy, and database security groups.
- Use multiple Availability Zones without opening broad CIDR ranges.
- Record and review VPC Flow Logs where appropriate.
Identity and Secrets
- Never use the AWS root user for administration.
- Grant least-privilege IAM permissions.
- Store database passwords in Secrets Manager and rotate them.
- Use separate MySQL users for applications, migrations, monitoring, and administration.
- Consider IAM DB authentication for suitable short-lived connections.
- Never embed secrets in AMIs, container images, repositories, or user data.
Encryption
- Enable KMS encryption when creating the DB instance.
- Require TLS for client connections.
- Encrypt snapshots, S3 exports, logs, and cross-Region copies.
- Use customer-managed KMS keys when your compliance or cross-account design requires control over key policy and rotation.
Auditing and Resilience
- Export supported MySQL logs to CloudWatch Logs.
- Enable CloudTrail for RDS, IAM, Secrets Manager, and KMS API activity.
- Enable deletion protection for production instances.
- Restrict snapshot sharing and regularly audit it.
- Use AWS Config and Security Hub controls where appropriate.
- Tag resources with owner, application, environment, cost centre, and data classification.
Design Disaster Recovery
Multi-AZ provides high availability inside one Region; it is not a complete regional disaster-recovery strategy.
Choose a DR pattern based on your recovery point objective (RPO), recovery time objective (RTO), data classification, and budget:
| Pattern | Typical recovery | Cost | Main consideration |
|---|---|---|---|
| Cross-Region snapshot copy | Longer RTO and RPO | Low | Simple, but restoration takes time |
| Cross-Region automated backup replication | PITR in another Region | Low to medium | Validate engine and Region support |
| Cross-Region read replica | Lower RPO and RTO | Medium to high | Asynchronous lag and manual promotion |
| Application-level multi-Region design | Lowest targets | Highest | Conflict handling and operational complexity |
Create a cross-Region read replica from the destination Region:
1 | aws rds create-db-instance-read-replica \ |
A failover runbook should include:
- Confirm the primary Region is unavailable or the database is unrecoverable.
- Freeze or isolate remaining writers when possible.
- Measure replication lag and record the expected data-loss window.
- Promote the replica.
- Update application configuration or DNS through an approved mechanism.
- Validate writes, reads, background jobs, and integrations.
- Communicate recovery status.
- Define how data will be reconciled and how the original Region will be rebuilt.
Test the runbook regularly. A diagram without a tested promotion, credential, networking, DNS, and application procedure is not a DR plan.
Understand Active-Active Replication
Active-active replication allows more than one database to accept writes. It can support specialised availability or geographic use cases, but it is much harder than a primary-and-replica design.
Before adopting it, answer:
- Can the same row be updated in two locations?
- How will primary keys remain globally unique?
- What happens when inserts, updates, or deletes conflict?
- Can every client tolerate eventual consistency?
- How will schema changes be coordinated?
- How will a failed node rejoin safely?
- How will replication loops be prevented?
RDS for MySQL supports managed active-active cluster capabilities for specific versions and Regions. Check current AWS engine and Region support before designing around the feature. For most applications, a single writer with Multi-AZ protection, read replicas, and a documented DR promotion process is simpler and safer.
Use active-active only when the business requirement justifies conflict management, operational complexity, higher cost, and more demanding testing.
Production Best Practices Summary
- Deploy databases only in private subnets.
- Use Multi-AZ for production availability.
- Encrypt storage and require TLS.
- Store and rotate credentials in Secrets Manager.
- Enable automated backups and test restores.
- Monitor AAS, waits, Top SQL, memory, storage, latency, and connections.
- Tune queries and indexes before scaling hardware.
- Use read replicas for read scaling, not as a substitute for Multi-AZ.
- Use RDS Proxy for bursty connection-heavy workloads.
- Test upgrades on restored or green environments.
- Manage parameter groups and infrastructure as code.
- Define measurable RPO and RTO targets.
- Test failover and regional recovery regularly.
- Create budgets, cost-allocation tags, and alarms.
Troubleshooting Guide
Cannot Connect to the RDS Endpoint
Check:
1 | nslookup "$DB_HOST" |
Then verify the client is inside a routable network, security groups reference the correct source, network ACLs allow return traffic, the database is available, and the hostname is the RDS endpoint rather than an old IP.
Access denied for user
Confirm the username, secret, MySQL host permissions, and authentication plugin. If the secret was rotated, ensure the application or connection pool has fetched the new value.
Too Many Connections
Inspect:
1 | SHOW GLOBAL STATUS LIKE 'Threads_connected'; |
Fix leaked connections, reduce pool sizes across service replicas, add timeouts, and consider RDS Proxy. Increasing max_connections without confirming available memory can make an outage worse.
High CPU
Use Top SQL, wait events, and execution plans. Check indexes, rows examined, query frequency, lock contention, temporary tables, and application retry loops before resizing the DB instance.
Storage Is Filling Up
Review binary log retention, general and slow logs, temporary tables, table growth, and unused data. Enable storage autoscaling before the database becomes critically low, but also fix the cause of unbounded growth.
Replica Lag Is Increasing
Check long-running transactions, write bursts, large DDL operations, destination instance capacity, network conditions, and replication errors. Do not route consistency-sensitive reads to a lagging replica.
Environment Cleanup
The following operations delete lab resources. Confirm every identifier before running them.
Remove the proxy:
1 | aws rds delete-db-proxy \ |
Delete replicas and restored test instances:
1 | aws rds delete-db-instance \ |
Disable deletion protection on the main lab instance and delete it:
1 | aws rds modify-db-instance \ |
Also remove resources that apply to your run:
- Manual snapshots and cross-Region snapshot copies
- PITR and restore-test instances
- Cross-Region replicas
- Snapshot export tasks and exported S3 objects
- Glue crawlers, Data Catalog tables, databases, and Athena query results
- CloudWatch alarms and log groups
- Secrets that are no longer required
- Custom IAM roles and policies
- Custom parameter groups after all attached instances are gone
- The DB subnet group and temporary security groups
- Temporary EC2 or CloudShell VPC environments
Never delete shared VPC, KMS, IAM, logging, or security resources simply because the lab used them.
Conclusion
Amazon RDS makes MySQL easier to operate, but a managed service does not automatically make a workload production-ready. Reliability comes from combining private networking, Multi-AZ, tested backups, useful monitoring, controlled upgrades, connection management, least-privilege access, and a rehearsed disaster-recovery plan.





