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"
}
}
Public RDS (Not Recommended)
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.
Name Description Type Default Required identifierRDS instance name stringn/a yes engineDatabase engine (postgres, mysql, mariadb) string"postgres"no engine_versionEngine version string"15"no instance_classInstance type string"db.t3.micro"no allocated_storageStorage in GB number20no max_allocated_storageMax storage for autoscaling (0 = disabled) number0no storage_typeStorage type (gp2, gp3, io1) string"gp2"no storage_encryptedEnable encryption booltrueno kms_key_idCustom KMS key ARN stringnullno db_nameDatabase name stringn/a yes usernameMaster username stringn/a yes passwordMaster password (auto-generated if null) stringnullno portDatabase port number5432no vpc_idVPC ID stringn/a yes subnet_idsSubnet IDs for DB subnet group list(string)n/a yes publicly_accessibleAllow public access boolfalseno allowed_security_group_idsSecurity groups allowed to connect list(string)[]no allowed_cidr_blocksCIDR blocks allowed to connect list(string)[]no multi_azEnable Multi-AZ boolfalseno backup_retention_periodBackup retention days number7no deletion_protectionEnable deletion protection boolfalseno performance_insights_enabledEnable Performance Insights boolfalseno familyParameter group family string"postgres15"no parametersCustom DB parameters list(object)[]no tagsTags to apply map(string){}no
Outputs
Name Description 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
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 < endpoin t > -U < usernam e > -d < databas e >
# 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 < endpoin t > -u < usernam e > -p < databas e >
# 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 )
import psycopg2
conn = psycopg2.connect(
host = endpoint,
port = port,
database = database,
user = username,
password = password,
sslmode = 'require'
)
const { Client } = require ( 'pg' )
const client = new Client ({
host: endpoint ,
port: port ,
database: database ,
user: username ,
password: password ,
ssl: { rejectUnauthorized: false }
})
await client . connect ()
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.
Instance Sizing
Workload Instance Class Storage Development db.t3.micro 20 GB gp2 Small Production db.t3.medium 100 GB gp3 Medium Production db.m5.large 500 GB gp3 Large Production db.m5.2xlarge 1 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
}
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-i d >
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 < endpoin t > -U < usernam e > -d < databas e >
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 / m o n t h ) ∗ ∗ P r o d u c t i o n : ∗ ∗ d b . m 5. l a r g e ( 15/month) **Production:** db.m5.large (~ 15/ m o n t h ) ∗ ∗ P ro d u c t i o n : ∗ ∗ d b . m 5. l a r g e ( 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