2026-01-27 17:35:45 -05:00
|
|
|
---
|
|
|
|
|
name: aws-rds
|
|
|
|
|
description: Provision and manage RDS databases. Configure backups, replication, and security. Use when deploying managed relational databases on AWS.
|
|
|
|
|
license: MIT
|
|
|
|
|
metadata:
|
|
|
|
|
author: devops-skills
|
|
|
|
|
version: "1.0"
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# AWS RDS
|
|
|
|
|
|
2026-03-24 18:02:50 -04:00
|
|
|
Deploy and manage Amazon RDS relational databases with production-grade backups, replication, monitoring, and security.
|
2026-01-27 17:35:45 -05:00
|
|
|
|
2026-03-24 18:02:50 -04:00
|
|
|
## When to Use This Skill
|
|
|
|
|
|
|
|
|
|
- Provisioning a managed PostgreSQL, MySQL, MariaDB, Oracle, or SQL Server database
|
|
|
|
|
- Setting up Multi-AZ deployments for high availability
|
|
|
|
|
- Creating read replicas for horizontal read scaling
|
|
|
|
|
- Configuring automated backups, snapshots, and point-in-time recovery
|
|
|
|
|
- Tuning database parameters for performance
|
|
|
|
|
- Migrating from self-managed databases to RDS
|
|
|
|
|
- Monitoring database performance and setting up alarms
|
|
|
|
|
|
|
|
|
|
## Prerequisites
|
|
|
|
|
|
|
|
|
|
- AWS CLI v2 installed and configured
|
|
|
|
|
- IAM permissions: `rds:*`, `ec2:DescribeSecurityGroups`, `ec2:DescribeSubnets`, `kms:*`, `cloudwatch:*`
|
|
|
|
|
- A VPC with at least two subnets in different AZs (for subnet group)
|
|
|
|
|
- Security group allowing database port access from application subnets only
|
|
|
|
|
|
|
|
|
|
## Create a DB Subnet Group
|
2026-01-27 17:35:45 -05:00
|
|
|
|
|
|
|
|
```bash
|
2026-03-24 18:02:50 -04:00
|
|
|
# Create a subnet group spanning two AZs
|
|
|
|
|
aws rds create-db-subnet-group \
|
|
|
|
|
--db-subnet-group-name production-db-subnets \
|
|
|
|
|
--db-subnet-group-description "Production database subnets" \
|
|
|
|
|
--subnet-ids subnet-private-a subnet-private-b
|
|
|
|
|
|
|
|
|
|
# List subnet groups
|
|
|
|
|
aws rds describe-db-subnet-groups \
|
|
|
|
|
--query "DBSubnetGroups[].{Name:DBSubnetGroupName,VPC:VpcId,Status:SubnetGroupStatus}" \
|
|
|
|
|
--output table
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Create a Production Database
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Create a PostgreSQL 16 Multi-AZ instance
|
2026-01-27 17:35:45 -05:00
|
|
|
aws rds create-db-instance \
|
2026-03-24 18:02:50 -04:00
|
|
|
--db-instance-identifier production-api-db \
|
|
|
|
|
--db-instance-class db.r6g.large \
|
2026-01-27 17:35:45 -05:00
|
|
|
--engine postgres \
|
2026-03-24 18:02:50 -04:00
|
|
|
--engine-version 16.4 \
|
|
|
|
|
--master-username appadmin \
|
|
|
|
|
--manage-master-user-password \
|
|
|
|
|
--allocated-storage 100 \
|
|
|
|
|
--max-allocated-storage 500 \
|
|
|
|
|
--storage-type gp3 \
|
2026-01-27 17:35:45 -05:00
|
|
|
--storage-encrypted \
|
2026-03-24 18:02:50 -04:00
|
|
|
--kms-key-id alias/rds-key \
|
|
|
|
|
--vpc-security-group-ids sg-db-access \
|
|
|
|
|
--db-subnet-group-name production-db-subnets \
|
|
|
|
|
--db-name appdb \
|
|
|
|
|
--backup-retention-period 14 \
|
|
|
|
|
--preferred-backup-window "03:00-04:00" \
|
|
|
|
|
--preferred-maintenance-window "sun:05:00-sun:06:00" \
|
|
|
|
|
--multi-az \
|
|
|
|
|
--auto-minor-version-upgrade \
|
|
|
|
|
--deletion-protection \
|
|
|
|
|
--copy-tags-to-snapshot \
|
|
|
|
|
--monitoring-interval 60 \
|
|
|
|
|
--monitoring-role-arn arn:aws:iam::123456789012:role/rds-monitoring-role \
|
|
|
|
|
--enable-performance-insights \
|
|
|
|
|
--performance-insights-retention-period 7 \
|
|
|
|
|
--enable-cloudwatch-logs-exports '["postgresql","upgrade"]' \
|
|
|
|
|
--tags '[
|
|
|
|
|
{"Key":"Environment","Value":"production"},
|
|
|
|
|
{"Key":"Team","Value":"backend"},
|
|
|
|
|
{"Key":"Backup","Value":"daily"}
|
|
|
|
|
]'
|
|
|
|
|
|
|
|
|
|
# Wait for instance to become available
|
|
|
|
|
aws rds wait db-instance-available --db-instance-identifier production-api-db
|
|
|
|
|
|
|
|
|
|
# Get connection endpoint
|
|
|
|
|
aws rds describe-db-instances \
|
|
|
|
|
--db-instance-identifier production-api-db \
|
|
|
|
|
--query "DBInstances[0].Endpoint.{Address:Address,Port:Port}" \
|
|
|
|
|
--output table
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Retrieve Master Password from Secrets Manager
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# When using --manage-master-user-password, RDS stores the password in Secrets Manager
|
|
|
|
|
aws rds describe-db-instances \
|
|
|
|
|
--db-instance-identifier production-api-db \
|
|
|
|
|
--query "DBInstances[0].MasterUserSecret.SecretArn" \
|
|
|
|
|
--output text
|
|
|
|
|
|
|
|
|
|
# Retrieve the secret value
|
|
|
|
|
aws secretsmanager get-secret-value \
|
|
|
|
|
--secret-id arn:aws:secretsmanager:us-east-1:123456789012:secret:rds-db-secret-abc123 \
|
|
|
|
|
--query SecretString --output text
|
2026-01-27 17:35:45 -05:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Parameter Groups
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-03-24 18:02:50 -04:00
|
|
|
# Create a custom parameter group
|
2026-01-27 17:35:45 -05:00
|
|
|
aws rds create-db-parameter-group \
|
2026-03-24 18:02:50 -04:00
|
|
|
--db-parameter-group-name production-pg16 \
|
|
|
|
|
--db-parameter-group-family postgres16 \
|
|
|
|
|
--description "Production PostgreSQL 16 parameters"
|
2026-01-27 17:35:45 -05:00
|
|
|
|
2026-03-24 18:02:50 -04:00
|
|
|
# Set performance parameters
|
2026-01-27 17:35:45 -05:00
|
|
|
aws rds modify-db-parameter-group \
|
2026-03-24 18:02:50 -04:00
|
|
|
--db-parameter-group-name production-pg16 \
|
|
|
|
|
--parameters \
|
|
|
|
|
"ParameterName=max_connections,ParameterValue=200,ApplyMethod=pending-reboot" \
|
|
|
|
|
"ParameterName=shared_buffers,ParameterValue={DBInstanceClassMemory/4},ApplyMethod=pending-reboot" \
|
|
|
|
|
"ParameterName=effective_cache_size,ParameterValue={DBInstanceClassMemory*3/4},ApplyMethod=pending-reboot" \
|
|
|
|
|
"ParameterName=work_mem,ParameterValue=65536,ApplyMethod=immediate" \
|
|
|
|
|
"ParameterName=maintenance_work_mem,ParameterValue=524288,ApplyMethod=immediate" \
|
|
|
|
|
"ParameterName=random_page_cost,ParameterValue=1.1,ApplyMethod=immediate" \
|
|
|
|
|
"ParameterName=log_min_duration_statement,ParameterValue=1000,ApplyMethod=immediate" \
|
|
|
|
|
"ParameterName=log_statement,ParameterValue=ddl,ApplyMethod=immediate" \
|
|
|
|
|
"ParameterName=idle_in_transaction_session_timeout,ParameterValue=60000,ApplyMethod=immediate"
|
2026-01-27 17:35:45 -05:00
|
|
|
|
2026-03-24 18:02:50 -04:00
|
|
|
# Apply parameter group to the instance
|
|
|
|
|
aws rds modify-db-instance \
|
|
|
|
|
--db-instance-identifier production-api-db \
|
|
|
|
|
--db-parameter-group-name production-pg16 \
|
|
|
|
|
--apply-immediately
|
2026-01-27 17:35:45 -05:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Read Replicas
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-03-24 18:02:50 -04:00
|
|
|
# Create a read replica in the same region
|
2026-01-27 17:35:45 -05:00
|
|
|
aws rds create-db-instance-read-replica \
|
2026-03-24 18:02:50 -04:00
|
|
|
--db-instance-identifier production-api-db-read1 \
|
|
|
|
|
--source-db-instance-identifier production-api-db \
|
|
|
|
|
--db-instance-class db.r6g.large \
|
|
|
|
|
--availability-zone us-east-1b \
|
|
|
|
|
--enable-performance-insights \
|
|
|
|
|
--monitoring-interval 60 \
|
|
|
|
|
--monitoring-role-arn arn:aws:iam::123456789012:role/rds-monitoring-role
|
|
|
|
|
|
|
|
|
|
# Create a cross-region read replica for DR
|
|
|
|
|
aws rds create-db-instance-read-replica \
|
|
|
|
|
--db-instance-identifier dr-api-db-read \
|
|
|
|
|
--source-db-instance-identifier arn:aws:rds:us-east-1:123456789012:db:production-api-db \
|
|
|
|
|
--db-instance-class db.r6g.large \
|
|
|
|
|
--region us-west-2 \
|
|
|
|
|
--storage-encrypted \
|
|
|
|
|
--kms-key-id alias/rds-dr-key
|
|
|
|
|
|
|
|
|
|
# Promote a read replica to standalone (for DR failover)
|
|
|
|
|
aws rds promote-read-replica \
|
|
|
|
|
--db-instance-identifier dr-api-db-read
|
|
|
|
|
|
|
|
|
|
# Check replication lag
|
|
|
|
|
aws cloudwatch get-metric-statistics \
|
|
|
|
|
--namespace AWS/RDS \
|
|
|
|
|
--metric-name ReplicaLag \
|
|
|
|
|
--dimensions Name=DBInstanceIdentifier,Value=production-api-db-read1 \
|
|
|
|
|
--start-time "$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)" \
|
|
|
|
|
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
|
|
|
--period 300 \
|
|
|
|
|
--statistics Average \
|
|
|
|
|
--output table
|
2026-01-27 17:35:45 -05:00
|
|
|
```
|
|
|
|
|
|
2026-03-24 18:02:50 -04:00
|
|
|
## Snapshots and Point-in-Time Recovery
|
2026-01-27 17:35:45 -05:00
|
|
|
|
2026-03-24 18:02:50 -04:00
|
|
|
```bash
|
|
|
|
|
# Create a manual snapshot
|
|
|
|
|
aws rds create-db-snapshot \
|
|
|
|
|
--db-instance-identifier production-api-db \
|
|
|
|
|
--db-snapshot-identifier production-api-db-pre-migration-$(date +%Y%m%d)
|
|
|
|
|
|
|
|
|
|
# Wait for snapshot to complete
|
|
|
|
|
aws rds wait db-snapshot-available \
|
|
|
|
|
--db-snapshot-identifier production-api-db-pre-migration-20260324
|
|
|
|
|
|
|
|
|
|
# Restore from snapshot (creates a new instance)
|
|
|
|
|
aws rds restore-db-instance-from-db-snapshot \
|
|
|
|
|
--db-instance-identifier production-api-db-restored \
|
|
|
|
|
--db-snapshot-identifier production-api-db-pre-migration-20260324 \
|
|
|
|
|
--db-instance-class db.r6g.large \
|
|
|
|
|
--db-subnet-group-name production-db-subnets \
|
|
|
|
|
--vpc-security-group-ids sg-db-access
|
|
|
|
|
|
|
|
|
|
# Point-in-time recovery (restore to a specific second)
|
|
|
|
|
aws rds restore-db-instance-to-point-in-time \
|
|
|
|
|
--source-db-instance-identifier production-api-db \
|
|
|
|
|
--target-db-instance-identifier production-api-db-pitr \
|
|
|
|
|
--restore-time "2026-03-24T10:30:00Z" \
|
|
|
|
|
--db-instance-class db.r6g.large \
|
|
|
|
|
--db-subnet-group-name production-db-subnets
|
|
|
|
|
|
|
|
|
|
# Copy snapshot to another region
|
|
|
|
|
aws rds copy-db-snapshot \
|
|
|
|
|
--source-db-snapshot-identifier arn:aws:rds:us-east-1:123456789012:snapshot:production-api-db-pre-migration-20260324 \
|
|
|
|
|
--target-db-snapshot-identifier production-api-db-dr-copy \
|
|
|
|
|
--region us-west-2 \
|
|
|
|
|
--kms-key-id alias/rds-dr-key
|
|
|
|
|
|
|
|
|
|
# Delete old snapshots
|
|
|
|
|
aws rds delete-db-snapshot --db-snapshot-identifier old-snapshot-name
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Monitoring and Alarms
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Set CPU utilization alarm
|
|
|
|
|
aws cloudwatch put-metric-alarm \
|
|
|
|
|
--alarm-name rds-production-cpu-high \
|
|
|
|
|
--alarm-description "RDS CPU > 80% for 5 minutes" \
|
|
|
|
|
--metric-name CPUUtilization \
|
|
|
|
|
--namespace AWS/RDS \
|
|
|
|
|
--dimensions Name=DBInstanceIdentifier,Value=production-api-db \
|
|
|
|
|
--statistic Average \
|
|
|
|
|
--period 300 \
|
|
|
|
|
--threshold 80 \
|
|
|
|
|
--comparison-operator GreaterThanThreshold \
|
|
|
|
|
--evaluation-periods 1 \
|
|
|
|
|
--alarm-actions arn:aws:sns:us-east-1:123456789012:db-alerts
|
|
|
|
|
|
|
|
|
|
# Set free storage space alarm (alert below 10 GB)
|
|
|
|
|
aws cloudwatch put-metric-alarm \
|
|
|
|
|
--alarm-name rds-production-storage-low \
|
|
|
|
|
--alarm-description "RDS free storage < 10GB" \
|
|
|
|
|
--metric-name FreeStorageSpace \
|
|
|
|
|
--namespace AWS/RDS \
|
|
|
|
|
--dimensions Name=DBInstanceIdentifier,Value=production-api-db \
|
|
|
|
|
--statistic Average \
|
|
|
|
|
--period 300 \
|
|
|
|
|
--threshold 10737418240 \
|
|
|
|
|
--comparison-operator LessThanThreshold \
|
|
|
|
|
--evaluation-periods 1 \
|
|
|
|
|
--alarm-actions arn:aws:sns:us-east-1:123456789012:db-alerts
|
|
|
|
|
|
|
|
|
|
# Check current database connections
|
|
|
|
|
aws cloudwatch get-metric-statistics \
|
|
|
|
|
--namespace AWS/RDS \
|
|
|
|
|
--metric-name DatabaseConnections \
|
|
|
|
|
--dimensions Name=DBInstanceIdentifier,Value=production-api-db \
|
|
|
|
|
--start-time "$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)" \
|
|
|
|
|
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
|
|
|
--period 300 \
|
|
|
|
|
--statistics Average Maximum \
|
|
|
|
|
--output table
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Terraform RDS Example
|
|
|
|
|
|
|
|
|
|
```hcl
|
|
|
|
|
resource "aws_db_subnet_group" "main" {
|
|
|
|
|
name = "production-db-subnets"
|
|
|
|
|
subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
|
|
|
|
|
|
|
|
|
|
tags = {
|
|
|
|
|
Environment = "production"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resource "aws_db_parameter_group" "postgres16" {
|
|
|
|
|
name = "production-pg16"
|
|
|
|
|
family = "postgres16"
|
|
|
|
|
|
|
|
|
|
parameter {
|
|
|
|
|
name = "max_connections"
|
|
|
|
|
value = "200"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parameter {
|
|
|
|
|
name = "shared_buffers"
|
|
|
|
|
value = "{DBInstanceClassMemory/4}"
|
|
|
|
|
apply_method = "pending-reboot"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parameter {
|
|
|
|
|
name = "log_min_duration_statement"
|
|
|
|
|
value = "1000"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resource "aws_db_instance" "main" {
|
|
|
|
|
identifier = "production-api-db"
|
|
|
|
|
engine = "postgres"
|
|
|
|
|
engine_version = "16.4"
|
|
|
|
|
instance_class = "db.r6g.large"
|
|
|
|
|
|
|
|
|
|
allocated_storage = 100
|
|
|
|
|
max_allocated_storage = 500
|
|
|
|
|
storage_type = "gp3"
|
|
|
|
|
storage_encrypted = true
|
|
|
|
|
kms_key_id = aws_kms_key.rds.arn
|
|
|
|
|
|
|
|
|
|
db_name = "appdb"
|
|
|
|
|
username = "appadmin"
|
|
|
|
|
manage_master_user_password = true
|
|
|
|
|
|
|
|
|
|
multi_az = true
|
|
|
|
|
db_subnet_group_name = aws_db_subnet_group.main.name
|
|
|
|
|
vpc_security_group_ids = [aws_security_group.db.id]
|
|
|
|
|
parameter_group_name = aws_db_parameter_group.postgres16.name
|
|
|
|
|
|
|
|
|
|
backup_retention_period = 14
|
|
|
|
|
backup_window = "03:00-04:00"
|
|
|
|
|
maintenance_window = "sun:05:00-sun:06:00"
|
|
|
|
|
copy_tags_to_snapshot = true
|
|
|
|
|
deletion_protection = true
|
|
|
|
|
skip_final_snapshot = false
|
|
|
|
|
final_snapshot_identifier = "production-api-db-final"
|
|
|
|
|
|
|
|
|
|
performance_insights_enabled = true
|
|
|
|
|
performance_insights_retention_period = 7
|
|
|
|
|
monitoring_interval = 60
|
|
|
|
|
monitoring_role_arn = aws_iam_role.rds_monitoring.arn
|
|
|
|
|
|
|
|
|
|
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
|
|
|
|
|
|
|
|
|
|
tags = {
|
|
|
|
|
Environment = "production"
|
|
|
|
|
Team = "backend"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resource "aws_db_instance" "read_replica" {
|
|
|
|
|
identifier = "production-api-db-read1"
|
|
|
|
|
replicate_source_db = aws_db_instance.main.identifier
|
|
|
|
|
instance_class = "db.r6g.large"
|
|
|
|
|
|
|
|
|
|
performance_insights_enabled = true
|
|
|
|
|
monitoring_interval = 60
|
|
|
|
|
monitoring_role_arn = aws_iam_role.rds_monitoring.arn
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Troubleshooting
|
|
|
|
|
|
|
|
|
|
| Problem | Cause | Fix |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| Cannot connect to RDS | Security group blocks traffic | Verify SG allows app subnet CIDR on DB port |
|
|
|
|
|
| Storage full | Auto-scaling not enabled or limit reached | Set `--max-allocated-storage`; increase manually |
|
|
|
|
|
| High replication lag | Write-heavy workload or replica undersized | Upgrade replica instance class; reduce write volume |
|
|
|
|
|
| Parameter change not applied | Requires reboot for static params | Reboot with `--force-failover` during maintenance window |
|
|
|
|
|
| Snapshot restore slow | Large database size | Use larger instance class for restore; consider PITR |
|
|
|
|
|
| Performance Insights empty | Not enabled or instance type unsupported | Enable PI; check instance class supports it |
|
|
|
|
|
| Multi-AZ failover happened | Hardware or AZ failure | Check RDS events; review failover logs |
|
|
|
|
|
| Connection count maxed out | Application connection leak | Implement connection pooling (PgBouncer/RDS Proxy) |
|
|
|
|
|
| Master password unknown | Using managed secret | Retrieve from Secrets Manager ARN in instance details |
|
2026-01-27 17:35:45 -05:00
|
|
|
|
|
|
|
|
## Related Skills
|
|
|
|
|
|
2026-03-24 18:02:50 -04:00
|
|
|
- [terraform-aws](../terraform-aws/) - IaC deployment for RDS
|
|
|
|
|
- [aws-vpc](../aws-vpc/) - Subnet groups and security groups
|
|
|
|
|
- [aws-iam](../aws-iam/) - RDS IAM authentication
|
|
|
|
|
- [aws-cost-optimization](../aws-cost-optimization/) - Reserved instances for RDS
|
|
|
|
|
- [aws-s3](../aws-s3/) - Export snapshots to S3
|