Kaizen

RDS Postgres

An encrypted Postgres 17 instance in private subnets that only accepts TLS connections.

GovCloudCMKView source
Module
rds-postgres
Layer
Data
Interface
23 inputs, 6 outputs
Used by
9 Kaizen apps

Why it matters

Most Kaizen apps keep their records in Postgres, and losing that data or exposing it is the most expensive failure a project can have. This module encrypts storage, refuses unencrypted connections, keeps a standby copy in a second availability zone, and blocks accidental deletion. Federal stacks add a customer managed key, log export, and DDL audit logging with five settings.

Use it when

  • The app needs a relational database, for example with Prisma.
  • A tool such as openobserve needs a metadata store that the caller manages.

Reach for something else when

  • You need a cache, session store, or queue with low latency. Use redis.
  • You store files, uploads, or exports. Use s3-bucket.

What it creates

  • aws_db_subnet_group (private subnets)
  • aws_security_group (port 5432 from allowed_security_group_ids and allowed_cidr_blocks only)
  • aws_db_parameter_group (rds.force_ssl = 1, optional DDL and slow query logging)
  • aws_db_instance (Postgres 17, gp3, encrypted, Multi-AZ, Performance Insights)

Secure by default

  • Storage is encrypted (storage_encrypted = true, not configurable).
  • Connections without TLS are refused (rds.force_ssl = 1).
  • A standby runs in a second availability zone (multi_az = true).
  • Deletion protection is on, and destroy takes a final snapshot (skip_final_snapshot = false).
  • Automated backups are kept for 7 days and Performance Insights is on.

Commercial and GovCloud

main.tf
module "rds" {
  source = "git::ssh://git@github.com/the-kaizen-labs/terraform-modules.git//rds-postgres?ref=v1.7.0"

  app_name           = local.app_name
  vpc_id             = module.networking.vpc_id
  private_subnet_ids = module.networking.private_subnet_ids

  allowed_security_group_ids = [module.ecs_service.security_group_id]

  instance_class = "db.t4g.micro"
  db_name        = "my_app"
  db_username    = "app_user"
  db_password    = data.aws_ssm_parameter.db_password.value

  tags = local.tags
}
SettingCommercial defaultFederal settingNote
kms_key_idnull (AWS managed RDS key)customer managed KMS key ARNFedRAMP SC-13.
performance_insights_kms_key_idnull (AWS managed)the same key as kms_key_id
enabled_cloudwatch_logs_exports[]["postgresql"]
enable_audit_loggingfalsetrueAdds log_statement = ddl and log_min_duration_statement = 1000.
copy_tags_to_snapshotfalsetrue

How it connects

  • From networking: vpc_id, private_subnet_ids
  • From ecs-service: security_group_id (as allowed_security_group_ids)
  • From bastion: security_group_id (as allowed_security_group_ids)
  • From openobserve: task_security_group_id (as allowed_security_group_ids)
  • To ecs-service: db_host, in an SSM DATABASE_URL
  • To openobserve: db_host, in the SSM DSN passed as metadata_postgres_dsn_ssm_arn

Inputs

NameTypeDefaultRequiredDescription
app_namestringyesApplication name used in resource naming
db_namestringyesDatabase name
db_passwordstringyesMaster password - pass this from an SSM data source, never hardcode Sensitive.
db_usernamestringyesMaster username
instance_classstringyesRDS instance class (e.g. db.t4g.micro, db.t4g.medium)
private_subnet_idslist(string)yesPrivate subnet IDs for the RDS subnet group
vpc_idstringyesVPC ID
allocated_storagenumber20noAllocated storage in GiB
allowed_cidr_blockslist(string)[]noCIDR blocks allowed to connect on port 5432. Use when security group creates a circular dependency.
allowed_security_group_idslist(string)[]noSecurity group IDs allowed to connect on port 5432 (use module.ecs_service.security_group_id)
backup_retention_daysnumber7noNumber of days to retain automated backups
copy_tags_to_snapshotboolfalsenoCopy instance tags to automated snapshots. AWS provider default is false; off here for v1 compatibility.
deletion_protectionbooltruenoProtect the instance from accidental deletion
enable_audit_loggingboolfalsenoAdd log_statement = ddl and log_min_duration_statement = 1000 to the parameter group (DDL audit + slow-query log). Off by default to avoid parameter group churn for v1 callers.
enabled_cloudwatch_logs_exportslist(string)[]noPostgres log types to export to CloudWatch (e.g. ["postgresql"]). Empty disables export. Federal posture should enable export to support the audit trail.
engine_versionstring"17"noPostgreSQL engine version
environmentstring""noEnvironment suffix (dev, prod, staging). Leave empty if the account has no env concept.
kms_key_idstringnullnoCustomer-managed KMS key ARN for storage encryption. Null falls back to the AWS-managed RDS key. Required for federal posture (FedRAMP SC-13).
multi_azbooltruenoEnable Multi-AZ for high availability
parameter_group_familystring"postgres17"noParameter group family. Must match engine_version (e.g. postgres17 for engine 17).
performance_insights_kms_key_idstringnullnoKMS key ARN for Performance Insights encryption. Null falls back to the AWS-managed key. Federal posture should set this to the same key as kms_key_id.
skip_final_snapshotboolfalsenoIf true, no final snapshot is taken when the instance is destroyed. Defaults false (federal-safe). Override to true for dev/POC databases that you genuinely want to nuke without leaving an orphan snapshot behind.
tagsmap(string){}noTags to apply to all resources

Outputs

NameDescription
db_endpointValue: aws_db_instance.this.endpoint
db_hostValue: aws_db_instance.this.address
db_instance_arnValue: aws_db_instance.this.arn
db_nameValue: aws_db_instance.this.db_name
db_portValue: aws_db_instance.this.port
security_group_idValue: aws_security_group.rds.id

Used by

AppPinned ref
BidBuddyv1.0.0
castlev1.6.0
dustv1.1.0
ginkgov1.3.0
juniperv1.1.0
magnoliav1.1.0
oakv1.1.0
poppyv1.1.0
saplingsv1.3.0

On this page