TerraformAWS
AWS RDS PostgreSQL
Production-ready PostgreSQL RDS instance with Multi-AZ, encryption, parameter group, and automated backups.
rdspostgresqldatabasemulti-az
Prerequisites
- •
VPC with private subnets (use aws-vpc template) - •
Security group allowing PostgreSQL (port 5432) from application tier - •
Terraform >= 1.5.0
Template Code
# ─────────────────────────────────────────────────────────────────────────────
# AWS RDS PostgreSQL (Multi-AZ, encrypted, automated backups)
# ─────────────────────────────────────────────────────────────────────────────
variable "environment" { default = "production" }
variable "db_name" { default = "appdb" }
variable "db_username" { default = "appuser" }
variable "db_password" { sensitive = true }
variable "instance_class" { default = "db.t3.medium" }
variable "allocated_storage" { default = 20 }
variable "private_subnet_ids" { type = list(string) }
variable "vpc_id" {}
variable "app_sg_id" {} # Security group of the application tier
# ── DB Subnet Group ───────────────────────────────────────────────────────────
resource "aws_db_subnet_group" "main" {
name = "${var.environment}-db-subnet-group"
subnet_ids = var.private_subnet_ids
tags = { Environment = var.environment }
}
# ── Security Group for RDS ────────────────────────────────────────────────────
resource "aws_security_group" "rds" {
name = "${var.environment}-rds-sg"
description = "PostgreSQL access from application tier"
vpc_id = var.vpc_id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [var.app_sg_id]
description = "PostgreSQL from app tier"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = { Environment = var.environment }
}
# ── Parameter Group ───────────────────────────────────────────────────────────
resource "aws_db_parameter_group" "postgres" {
name = "${var.environment}-postgres16"
family = "postgres16"
parameter {
name = "log_connections"
value = "1"
}
parameter {
name = "log_min_duration_statement"
value = "1000" # Log queries > 1s
}
parameter {
name = "shared_preload_libraries"
value = "pg_stat_statements"
}
}
# ── RDS Instance ──────────────────────────────────────────────────────────────
resource "aws_db_instance" "main" {
identifier = "${var.environment}-postgres"
engine = "postgres"
engine_version = "16.2"
instance_class = var.instance_class
allocated_storage = var.allocated_storage
db_name = var.db_name
username = var.db_username
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.main.name
parameter_group_name = aws_db_parameter_group.postgres.name
vpc_security_group_ids = [aws_security_group.rds.id]
# High availability
multi_az = true
# Storage
storage_type = "gp3"
storage_encrypted = true
max_allocated_storage = 100 # Enable autoscaling up to 100GB
# Backups
backup_retention_period = 7
backup_window = "03:00-04:00"
maintenance_window = "Mon:04:00-Mon:05:00"
delete_automated_backups = false
skip_final_snapshot = false
final_snapshot_identifier = "${var.environment}-postgres-final"
# Protection
deletion_protection = true
# Monitoring
monitoring_interval = 60
performance_insights_enabled = true
tags = { Environment = var.environment }
}
output "db_endpoint" { value = aws_db_instance.main.endpoint }
output "db_name" { value = aws_db_instance.main.db_name }
Usage
terraform init terraform apply -var="db_password=CHANGE_ME" -var="private_subnet_ids=["subnet-xxx","subnet-yyy"]"