SynfraCore
Synfracore
Start Learning
Navigation

Academies

Platform

RoadmapsLabsCertificationsInterviewPYQsAI AssistantCareer
Start Learning Free🗺️ Learning Roadmaps

RDS & AuroraFundamentals

Core concepts and commands — hands-on from the start

✍️
Written by senior engineers. Reviewed for technical accuracy.· Updated 2025 · SynfraCore RDS & Aurora Team
Expert Content

AWS RDS & Aurora — Fundamentals

Every application eventually needs somewhere durable to put its data, and someone has to keep that database patched, backed up, and running when a server dies at 3am. RDS is AWS's answer: you still design your schema and write your queries exactly like you would against a database you host yourself, but AWS takes over the operational parts — provisioning the underlying server, applying OS/engine patches, taking backups, and handling failover if hardware fails.

Quick Analogy

Think of RDS the way you'd think of hiring a building superintendent instead of doing all your own home maintenance: you still decide how to furnish and use each room (your schema, your queries, your indexes), but the "super" (AWS) handles the boiler, the plumbing, and calling a backup unit if something breaks overnight. Aurora takes this further — it's like moving into a building that was engineered from the ground up for reliability (distributed storage across multiple AZs) instead of a converted regular house.

RDS Overview

RDS = Managed relational database service
AWS handles: Hardware, OS, patching, backups, replication, failover

Supported engines:
  PostgreSQL 11-16
  MySQL 5.7, 8.0
  MariaDB 10.5-10.11
  Oracle (bring your own license)
  SQL Server (Standard/Enterprise)
  Aurora (MySQL-compatible or PostgreSQL-compatible)

(needs verification — these are the engine major/minor versions available at time of writing; AWS regularly adds new versions and deprecates old ones, so check the current "supported engine versions" page before relying on this list.)

How the Pieces Fit Together

             Client / App tier (private subnet)
                        │
                        ▼
        ┌───────────────────────────────┐
        │   Security Group (rds SG)      │   inbound 5432 from app SG only
        └───────────────┬────────────────┘
                        ▼
        ┌───────────────────────────────┐
        │   DB Subnet Group               │   spans private subnets in 2+ AZs
        │   ┌───────────┐  ┌───────────┐ │
        │   │ Primary    │  │ Standby   │ │  ← Multi-AZ: sync replication,
        │   │ (AZ-1)     │  │ (AZ-2)    │ │    standby not readable
        │   └───────────┘  └───────────┘ │
        └───────────────────────────────┘

The subnet group, security group, and Multi-AZ standby are the three pieces every production RDS instance needs — miss any one and you either have no HA, an unreachable database, or an accidentally public one.

Create RDS Instance (Terraform)

hcl
resource "aws_db_subnet_group" "main" {
  name       = "prod-db-subnets"
  subnet_ids = var.private_subnet_ids
}

resource "aws_security_group" "rds" {
  name   = "prod-rds-sg"
  vpc_id = var.vpc_id
  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [var.app_sg_id]  # Only app servers can connect
  }
}

resource "aws_db_instance" "postgres" {
  identifier        = "prod-postgres"
  engine            = "postgres"
  engine_version    = "16.1"
  instance_class    = "db.r6g.large"    # Graviton — 20% cheaper
  allocated_storage = 100
  storage_type      = "gp3"
  storage_encrypted = true              # Always encrypt!
  kms_key_id        = aws_kms_key.rds.arn

  db_name  = "myapp"
  username = "postgres"
  password = random_password.db.result  # Use Secrets Manager in prod

  multi_az               = true         # Automatic failover to standby
  db_subnet_group_name   = aws_db_subnet_group.main.name
  vpc_security_group_ids = [aws_security_group.rds.id]

  backup_retention_period = 7           # 7 days automated backups
  backup_window           = "03:00-04:00"  # UTC
  maintenance_window      = "sun:04:00-sun:05:00"

  deletion_protection = true
  skip_final_snapshot = false
  final_snapshot_identifier = "prod-postgres-final"

  performance_insights_enabled          = true
  performance_insights_retention_period = 7
  monitoring_interval                   = 60  # Enhanced monitoring
  monitoring_role_arn                   = aws_iam_role.rds_enhanced.arn

  tags = { Environment = "prod" }
}

Aurora vs RDS

RDS PostgreSQL/MySQL:
  Standard PostgreSQL/MySQL — fully compatible
  Single-AZ or Multi-AZ (1 standby)
  Storage: EBS volumes, auto-scaling triggers when free space is low *(needs verification — exact increment/threshold formula for storage autoscaling, check current AWS RDS docs)*
  Replicas: Up to 5 read replicas

Aurora:
  Custom storage engine built for cloud
  Shared storage across 6 AZ copies — 6 copies always exist
  Replicas: Up to 15 read replicas (any is primary-capable)
  Aurora Serverless v2: Auto-scales compute 0.5-128 ACUs
  Compatible with MySQL 8.0 or PostgreSQL 14/15/16
  
  Aurora advantages:
  - 5× throughput vs MySQL, 3× vs PostgreSQL
  - Storage auto-grows to 128TB
  - Recovery in ~30 seconds (vs ~60-120 seconds/1-2 minutes for RDS Multi-AZ)
  - Backtrack: Rewind database to any second in last 72h (Aurora MySQL only —
    Aurora PostgreSQL does not support Backtrack)

  Aurora disadvantages:
  - More expensive (about 20% higher than equivalent RDS)
  - Aurora-specific features break standard compatibility

RDS Proxy

hcl
# RDS Proxy — connection pooler for serverless and Lambda
resource "aws_db_proxy" "main" {
  name                   = "prod-rds-proxy"
  debug_logging          = false
  engine_family          = "POSTGRESQL"
  idle_client_timeout    = 1800
  require_tls            = true
  role_arn               = aws_iam_role.rds_proxy.arn
  vpc_security_group_ids = [aws_security_group.rds_proxy.id]
  vpc_subnet_ids         = var.private_subnet_ids

  auth {
    iam_auth    = "REQUIRED"        # Lambda uses IAM auth — no passwords!
    secret_arn  = aws_secretsmanager_secret.db.arn
  }

  target_group_name = "default"  # Links to RDS instance automatically
}

Maintenance and Operations

bash
# Check slow queries (PostgreSQL RDS)
# Enable pg_stat_statements in parameter group first
psql -h $DB_HOST -U postgres -d myapp -c "
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;"

# RDS Performance Insights — best tool for slow query analysis
# AWS Console → RDS → Performance Insights → SQL tab

# Manual snapshot (before risky migrations)
aws rds create-db-snapshot \
    --db-instance-identifier prod-postgres \
    --db-snapshot-identifier before-migration-$(date +%Y%m%d)

# Promote read replica (for failover testing or migration)
aws rds promote-read-replica --db-instance-identifier prod-replica

# Modify instance class (causes reboot or failover)
aws rds modify-db-instance \
    --db-instance-identifier prod-postgres \
    --db-instance-class db.r6g.xlarge \
    --apply-immediately  # or --no-apply-immediately for maintenance window

Try It Yourself (2 minutes)

If you have access to any RDS instance (even a free-tier db.t3.micro), run:

bash
aws rds describe-db-instances \
    --query 'DBInstances[*].[DBInstanceIdentifier,MultiAZ,PubliclyAccessible,Endpoint.Address]' \
    --output table

Check the PubliclyAccessible column specifically. For any real database it should read False — if you ever see True on a production instance, that's a database reachable from the open internet and worth fixing immediately, regardless of what the security group says.

Share:
Join our Community
Daily tips, job alerts, interview help — join engineers learning together
Up Next
RDS & AuroraIntermediate
Real-world patterns, best practices, and deeper topics
Also Worth Exploring
← Back to all RDS & Aurora modules
InstallationIntermediate