Skip to main content

Overview

The RDS module creates Amazon RDS database instances (PostgreSQL, MySQL, or MariaDB) with security groups, subnet groups, parameter groups, and automated password generation.

Features

Multiple Database Engines

Support for PostgreSQL, MySQL, and MariaDB

Automated Backups

Configurable backup retention and maintenance windows

Encryption

Encryption at rest with KMS (enabled by default)

Security Groups

Automatic security group with CIDR and security group ID allow lists

Password Generation

Automatic secure password generation if not provided

Multi-AZ

Optional Multi-AZ deployment for high availability

Usage Examples

Basic PostgreSQL Instance

module "rds" {
  source = "[email protected]:opsnorth/terraform-modules.git//rds?ref=v1.0.0"

  identifier = "dev-database"
  db_name    = "myapp"
  username   = "admin"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnet_ids

  allowed_security_group_ids = [module.eks.cluster_security_group_id]

  tags = {
    Environment = "dev"
  }
}
If password is not provided, a random 24-character password is generated and stored in Terraform state as a sensitive output.

Production PostgreSQL with Multi-AZ

module "rds" {
  source = "[email protected]:opsnorth/terraform-modules.git//rds?ref=v1.0.0"

  identifier = "prod-database"
  db_name    = "production"
  username   = "admin"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnet_ids

  engine         = "postgres"
  engine_version = "15"
  instance_class = "db.m5.large"

  # Storage
  allocated_storage     = 100
  max_allocated_storage = 500  # Auto-scaling up to 500 GB
  storage_type          = "gp3"
  storage_encrypted     = true

  # High availability
  multi_az = true

  # Backups
  backup_retention_period = 30
  deletion_protection     = true

  # Performance Insights
  performance_insights_enabled = true

  # Network access
  allowed_security_group_ids = [
    module.eks.cluster_security_group_id,
    module.bastion.security_group_id
  ]

  tags = {
    Environment = "production"
    Critical    = "true"
  }
}

MySQL Database

module "rds" {
  source = "[email protected]:opsnorth/terraform-modules.git//rds?ref=v1.0.0"

  identifier = "mysql-db"
  db_name    = "wordpress"
  username   = "wpuser"

  engine         = "mysql"
  engine_version = "8.0"
  port           = 3306
  family         = "mysql8.0"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnet_ids

  instance_class    = "db.t3.medium"
  allocated_storage = 50

  allowed_cidr_blocks = ["10.0.0.0/16"]

  tags = {
    Application = "wordpress"
  }
}

With Custom Parameters

module "rds" {
  source = "[email protected]:opsnorth/terraform-modules.git//rds?ref=v1.0.0"

  identifier = "custom-postgres"
  db_name    = "appdb"
  username   = "dbadmin"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnet_ids

  engine  = "postgres"
  family  = "postgres15"

  # Custom PostgreSQL parameters
  parameters = [
    {
      name  = "max_connections"
      value = "200"
    },
    {
      name  = "shared_buffers"
      value = "256MB"
    },
    {
      name  = "work_mem"
      value = "16MB"
    },
    {
      name  = "maintenance_work_mem"
      value = "128MB"
    },
    {
      name  = "log_statement"
      value = "all"
    }
  ]

  allowed_security_group_ids = [module.eks.cluster_security_group_id]

  tags = {
    Environment = "staging"
  }
}
module "rds" {
  source = "[email protected]:opsnorth/terraform-modules.git//rds?ref=v1.0.0"

  identifier = "public-db"
  db_name    = "testdb"
  username   = "admin"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.public_subnet_ids  # Public subnets

  publicly_accessible = true

  # Restrict access by IP
  allowed_cidr_blocks = [
    "203.0.113.0/24"  # Your office IP range
  ]

  tags = {
    Environment = "demo"
  }
}
Publicly accessible RDS instances should only be used for testing. Always use private subnets for production databases.

Inputs

NameDescriptionTypeDefaultRequired
identifierRDS instance namestringn/ayes
engineDatabase engine (postgres, mysql, mariadb)string"postgres"no
engine_versionEngine versionstring"15"no
instance_classInstance typestring"db.t3.micro"no
allocated_storageStorage in GBnumber20no
max_allocated_storageMax storage for autoscaling (0 = disabled)number0no
storage_typeStorage type (gp2, gp3, io1)string"gp2"no
storage_encryptedEnable encryptionbooltrueno
kms_key_idCustom KMS key ARNstringnullno
db_nameDatabase namestringn/ayes
usernameMaster usernamestringn/ayes
passwordMaster password (auto-generated if null)stringnullno
portDatabase portnumber5432no
vpc_idVPC IDstringn/ayes
subnet_idsSubnet IDs for DB subnet grouplist(string)n/ayes
publicly_accessibleAllow public accessboolfalseno
allowed_security_group_idsSecurity groups allowed to connectlist(string)[]no
allowed_cidr_blocksCIDR blocks allowed to connectlist(string)[]no
multi_azEnable Multi-AZboolfalseno
backup_retention_periodBackup retention daysnumber7no
deletion_protectionEnable deletion protectionboolfalseno
performance_insights_enabledEnable Performance Insightsboolfalseno
familyParameter group familystring"postgres15"no
parametersCustom DB parameterslist(object)[]no
tagsTags to applymap(string){}no

Outputs

NameDescription
db_instance_idRDS instance ID
db_instance_endpointConnection endpoint (host:port)
db_instance_addressHostname
db_instance_portPort
db_instance_nameDatabase name
db_instance_usernameMaster username (sensitive)
db_instance_passwordMaster password (sensitive)
security_group_idSecurity group ID
connection_stringPostgreSQL connection string template

Connection Strings

Using Terraform Outputs

output "db_connection_info" {
  value = {
    host     = module.rds.db_instance_address
    port     = module.rds.db_instance_port
    database = module.rds.db_instance_name
    username = module.rds.db_instance_username
    password = module.rds.db_instance_password
  }
  sensitive = true
}

output "psql_command" {
  value     = "psql -h ${module.rds.db_instance_address} -U ${module.rds.db_instance_username} -d ${module.rds.db_instance_name}"
  sensitive = true
}

PostgreSQL Connection

# Using psql
psql -h <endpoint> -U <username> -d <database>

# Connection string
postgresql://<username>:<password>@<endpoint>:<port>/<database>

# With SSL
postgresql://<username>:<password>@<endpoint>:<port>/<database>?sslmode=require

MySQL Connection

# Using mysql client
mysql -h <endpoint> -u <username> -p <database>

# Connection string
mysql://<username>:<password>@<endpoint>:<port>/<database>

From Application

import "database/sql"
import _ "github.com/lib/pq"

connStr := fmt.Sprintf(
    "postgres://%s:%s@%s:%d/%s?sslmode=require",
    username, password, host, port, database,
)

db, err := sql.Open("postgres", connStr)

Security Best Practices

Private Subnets

Always deploy RDS in private subnets without public access.

Encryption

Enable encryption at rest using KMS keys for sensitive data.

Strong Passwords

Use auto-generated passwords or strong custom passwords (min 24 characters).

Least Privilege

Restrict security group access to only required sources.

Backup Retention

Set appropriate backup retention (7-35 days for production).

Deletion Protection

Enable deletion protection for production databases.

Performance Tuning

Instance Sizing

WorkloadInstance ClassStorage
Developmentdb.t3.micro20 GB gp2
Small Productiondb.t3.medium100 GB gp3
Medium Productiondb.m5.large500 GB gp3
Large Productiondb.m5.2xlarge1 TB io1

Storage Auto-Scaling

module "rds" {
  source = "[email protected]:opsnorth/terraform-modules.git//rds?ref=v1.0.0"

  allocated_storage     = 100   # Initial size
  max_allocated_storage = 500   # Max auto-scale size
  storage_type          = "gp3" # Better IOPS than gp2
}

Performance Insights

Enable for production databases:
module "rds" {
  source = "[email protected]:opsnorth/terraform-modules.git//rds?ref=v1.0.0"

  performance_insights_enabled = true
}
View insights in AWS Console:
RDS → Databases → <instance> → Monitoring → Performance Insights

Backup and Recovery

Automated Backups

module "rds" {
  source = "[email protected]:opsnorth/terraform-modules.git//rds?ref=v1.0.0"

  backup_retention_period = 30  # Keep backups for 30 days
  
  # Optional: Specify backup window (UTC)
  preferred_backup_window = "03:00-04:00"
}

Manual Snapshot

aws rds create-db-snapshot \
  --db-instance-identifier prod-database \
  --db-snapshot-identifier prod-database-snapshot-$(date +%Y%m%d)

Restore from Snapshot

aws rds restore-db-instance-from-db-snapshot \
  --db-instance-identifier restored-database \
  --db-snapshot-identifier prod-database-snapshot-20240115

Troubleshooting

Cannot Connect to Database

Check security group rules:
aws ec2 describe-security-groups \
  --group-ids <security-group-id>
Verify subnet routing:
aws ec2 describe-route-tables \
  --filters "Name=association.subnet-id,Values=<subnet-id>"
Test connection from EC2:
# SSH to EC2 instance in same VPC
psql -h <endpoint> -U <username> -d <database>

Performance Issues

Check Performance Insights:
  • RDS Console → Performance Insights
  • Look for slow queries and high wait events
Review CloudWatch metrics:
aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name CPUUtilization \
  --dimensions Name=DBInstanceIdentifier,Value=prod-database \
  --start-time 2024-01-15T00:00:00Z \
  --end-time 2024-01-15T23:59:59Z \
  --period 3600 \
  --statistics Average

Storage Full

Enable auto-scaling:
max_allocated_storage = 500  # Allows scaling up to 500 GB
Or increase manually:
aws rds modify-db-instance \
  --db-instance-identifier prod-database \
  --allocated-storage 200 \
  --apply-immediately

Cost Optimization

Start with smaller instances and scale based on CloudWatch metrics. Over-provisioning wastes money.Development: db.t3.micro (~15/month)Production:db.m5.large( 15/month) **Production:** db.m5.large (~150/month)
gp3 provides better baseline performance at lower cost:
  • 100 GB gp2: ~$11.50/month
  • 100 GB gp3: ~$10.00/month
Shorter retention saves on backup storage costs:
  • Development: 7 days
  • Production: 14-30 days
Multi-AZ doubles RDS costs. Only use in production:
  • Single-AZ: $150/month
  • Multi-AZ: $300/month

VPC Module

Create VPC and subnets for RDS

EKS Module

Connect RDS to EKS workloads

K8s Scheduler Configuration

Configure database connection

Infrastructure Guide

Complete deployment workflow

Build docs developers (and LLMs) love