Skip to main content
Data Storage Modules 1.4.0Last updated in version 1.2.0

RDS Read Replicas Module

View Source Release Notes

This module creates a read replica (read-only copy) of a DB instance.

About RDS Read Replicas​

A read replica is a read-only copy of a DB instance. You can reduce the load on your primary DB instance by routing queries from your applications to the read replica. Refer to Working with DB instance read replicas for more information.

Upgrading the engine version on a replica​

Set engine_version to upgrade replicas that already exist. Leave it null, the default, and each replica reports whatever version AWS gives it, which is the version it inherited from the primary.

Two limits to know before you rely on it:

  • It does nothing when a replica is created. The provider sends the version on a modification only, not on the create call that clones the source instance, so a replica created while engine_version is set comes up on the source instance's version and a second apply moves it. Raising num_read_replicas part way through an upgrade therefore takes two applies.
  • It is held until the maintenance window unless apply_immediately is true. With the default, false, the apply returns green and the replica has not moved.

You need this wherever AWS requires the replicas to be upgraded before the source instance:

  • PostgreSQL, minor upgrades: "If your database has read replicas, you must first upgrade all of the read replicas before you upgrade the source instance or cluster" (Upgrades of the RDS for PostgreSQL DB engine, checked 7 September 2026).
  • MySQL and MariaDB, minor and major upgrades alike: "If your MySQL DB instance uses read replicas, then you must upgrade all of the read replicas before upgrading the source instance" (Upgrading a MySQL DB instance, checked 7 September 2026). That page states the rule with no minor or major qualifier.

Upgrading the source instance first fails with DBUpgradeDependencyFailure: One or more of the DB Instance's read replicas need to be upgraded. So it takes two applies:

  1. Set engine_version on this module to the target version and apply_immediately to true, leave the source instance's engine_version where it is, and apply. Confirm every replica moved by asking AWS rather than Terraform, aws rds describe-db-instances --db-instance-identifier <replica identifier> --query 'DBInstances[0].EngineVersion'. Do not gate the next step on the read_replica_engine_versions output: it reports the version Terraform state held at the last refresh, so straight after this apply it can still show the old one even though the replica has upgraded, and it catches up on the next refresh or apply. Every replica restarts at once, since they are one count resource with no ordering between the instances, so -target them one index at a time if reads have to stay served.
  2. Move the source instance's engine_version to the target version and apply.

Then clear engine_version back to null. That is a no-op against the state you just reached, because engine_version on aws_db_instance is a computed attribute, so leaving it unset tracks whatever AWS reports. Leaving a full MAJOR.MINOR.PATCH version pinned instead invites drift: with auto_minor_version_upgrade = true, the default, AWS moves the replicas to a newer patch during a maintenance window, and the next plan then proposes moving them back down to the pinned value.

A PostgreSQL major version upgrade is the exception, and the answer there depends on where the replica lives:

  • In-Region replicas: leave engine_version at null. AWS does the work: "If you upgrade a DB instance that has in-Region read replicas, Amazon RDS upgrades the replicas along with the primary DB instance" (PostgreSQL page above, same date). A pinned old version is a trap here, because AWS moves the replicas forward with the primary and the next plan then proposes moving them back down to the pinned value.
  • Cross-region replicas: that rule covers in-Region replicas only. A replica created by a separate call to this module against a provider in another region is its own aws_db_instance, and it moves only when you set engine_version on it, so give it its own apply.

Promoting Read Replica as Primary​

For disaster recovery, you may need to promote a read replica as a primary instance. Promoting an RDS replica to be a primary instance is not a supported operation in Terraform and requires direct interaction with AWS APIs. Subsequent steps are then required to ensure your Terraform state is up-to-date. Please see the following walk-through for steps to promote a replica to primary.

  1. Promote the replica: Run AWS CLI command to promote read replica as primary with the following command:
aws rds promote-read-replica \
--db-instance-identifier <replica-identifier> \
--region <region>

Note: This command triggers the promotion process and takes an additional 30-60 mins to complete the modification.

  1. Update the Terraform configuration: Modify your Terraform configuration file (e.g., main.tf) to reflect the new state. There's no way to automatically reflect the new state in terraform.
  2. Import the new primary instance into Terraform state: Run the terraform import command. This command associates the resource in your Terraform configuration with the existing resource in AWS.
terraform import aws_db_instance.<identifier> <primary_instance_arn>
  1. Refresh the Terraform state: Run terraform refresh to fetch the current state of the primary instance from AWS and update the Terraform state file accordingly. This ensures that Terraform is aware of the new primary instance's attributes.
  2. Verify the Terraform plan: Run terraform plan to review the changes that Terraform will apply based on the updated configuration and current state. Make sure the plan reflects the desired changes, including the new primary instance attributes.
  3. Apply the Terraform changes: If the plan looks correct, apply the changes by running terraform apply. Terraform will update the resources to match the desired configuration, reflecting the newly promoted primary instance.

Sample Usage​

main.tf

# ------------------------------------------------------------------------------------------------------
# DEPLOY GRUNTWORK'S RDS-REPLICAS MODULE
# ------------------------------------------------------------------------------------------------------

module "rds_replicas" {

source = "git::git@github.com:gruntwork-io/terraform-aws-data-storage.git//modules/rds-replicas?ref=v1.4.0"

# ----------------------------------------------------------------------------------------------------
# REQUIRED VARIABLES
# ----------------------------------------------------------------------------------------------------

# The instance type to use for the db (e.g. db.t2.micro)
instance_type = <string>

# The name used to namespace all resources created by these templates,
# including the DB instance (e.g. drupaldb). Must be unique for this region.
# May contain only lowercase alphanumeric characters, hyphens, underscores,
# periods, and spaces.
name = <string>

# The port the DB will listen on (e.g. 3306)
port = <number>

# An ID of the primary DB instance to create read replicas from
primary_instance_id = <string>

# The id of the VPC in which this DB should be deployed.
vpc_id = <string>

# ----------------------------------------------------------------------------------------------------
# OPTIONAL VARIABLES
# ----------------------------------------------------------------------------------------------------

# List of IDs of AWS Security Groups to attach to the read replica RDS
# instance.
additional_security_group_ids = []

# A list of CIDR-formatted IP address ranges that can connect to read replica
# instances. If not set read replica instances will use the same security
# group as master instance.
allow_connections_from_cidr_blocks = []

# A list of IPv6 CIDR-formatted IP address ranges that can connect to read
# replica instances. If not set read replica instances will use the same
# security group as master instance.
allow_connections_from_ipv6_cidr_blocks = []

# A list of Security Groups that can connect to read replica instances. If not
# set read replica instances will use the same security group as master
# instance.
allow_connections_from_security_groups = []

# Indicates whether major version upgrades (e.g. 9.4.x to 9.5.x) will ever be
# permitted. Note that these updates must always be manually performed and
# will never automatically applied.
allow_major_version_upgrade = true

# The availability zones within which it should be possible to spin up
# replicas
allowed_replica_zones = []

# Specifies whether any cluster modifications are applied immediately, or
# during the next maintenance window. Note that cluster modifications may
# cause degraded performance or downtime.
apply_immediately = false

# Indicates that minor engine upgrades will be applied automatically to the DB
# instance during the maintenance window. If set to true, you should set
# var.engine_version to MAJOR.MINOR and omit the .PATCH at the end (e.g., use
# 5.7 and not 5.7.11); otherwise, you'll get Terraform state drift. See
# https://www.terraform.io/docs/providers/aws/r/db_instance.html#engine_version
# for more details.
auto_minor_version_upgrade = true

# The description of the aws_db_subnet_group that is created. Defaults to
# 'Subnet group for the var.name DB' if not specified.
aws_db_subnet_group_description = null

# The name of the aws_db_subnet_group that is created, or an existing one to
# use if create_subnet_group is false. Defaults to var.name if not specified.
aws_db_subnet_group_name = null

# How many days to keep backup snapshots around before cleaning them up. Must
# be 1 or greater to support read replicas. 0 means disable automated backups.
backup_retention_period = 21

# The daily time range during which automated backups are created (e.g.
# 04:00-09:00). Time zone is UTC. Performance may be degraded while a backup
# runs.
backup_window = null

# The Certificate Authority (CA) certificates bundle to use on the RDS
# instance.
ca_cert_identifier = null

# Copy all the RDS instance tags to snapshots. Default is false.
copy_tags_to_snapshot = false

# When working with read replicas, only configure db subnet group if the
# source database specifies an instance in another AWS Region. If true, it
# will create a new subnet group.
create_subnet_group = false

# Timeout for DB creating
creating_timeout = "40m"

# A map of custom tags to apply to the RDS Instance and the Security Group
# created for it. The key is the tag name and the value is the tag value.
custom_tags = {}

# Specifies whether to remove automated backups immediately after the DB
# instance is deleted
delete_automated_backups = null

# Timeout for DB deleting
deleting_timeout = "60m"

# The database can't be deleted when this value is set to true. The default is
# false.
deletion_protection = false

# List of log types to enable for exporting to CloudWatch logs. If omitted, no
# logs will be exported. Valid values (depending on engine): alert, audit,
# error, general, listener, slowquery, trace, postgresql (PostgreSQL) and
# upgrade (PostgreSQL).
enabled_cloudwatch_logs_exports = []

# The engine version to run on the read replicas (e.g. 16.14 for postgres). If
# null, each replica reports whatever version AWS gives it, which is the
# version it inherited from the primary. Set this to upgrade existing replicas
# ahead of the source instance, which AWS requires for a minor version upgrade
# on any engine, and on MySQL and MariaDB for a major version upgrade too.
# Three caveats: it has no effect when a replica is created, since a new
# replica always comes up on the source instance version and a second apply
# moves it; the change is held until the maintenance window unless
# var.apply_immediately is true; and while var.auto_minor_version_upgrade is
# true, prefer MAJOR.MINOR over a full patch version and clear this back to
# null once the upgrade is done, or AWS patching will drift away from the
# pinned value. Leave it null for a PostgreSQL major version upgrade of an
# in-Region replica: AWS upgrades those along with the primary. See this
# module README for the sequence.
engine_version = null

# Specifies whether IAM database authentication is enabled. This option is
# only available for MySQL and PostgreSQL engines.
iam_database_authentication_enabled = null

# The amount of provisioned IOPS for the primary instance. Setting this
# implies a storage_type of 'io1','io2, or 'gp3'. Set to 0 to disable.
iops = 0

# The ARN of a KMS key that should be used to encrypt data on disk. Only used
# if var.storage_encrypted is true. If you leave this blank, the default RDS
# KMS key for the account will be used.
kms_key_arn = null

# The weekly day and time range during which system maintenance can occur
# (e.g. wed:04:00-wed:04:30). Time zone is UTC. Performance may be degraded or
# there may even be a downtime during maintenance windows.
maintenance_window = null

# When configured, the upper limit to which Amazon RDS can automatically scale
# the storage of the DB instance. Configuring this will automatically ignore
# differences to allocated_storage. Must be greater than or equal to
# allocated_storage or 0 to disable Storage Autoscaling.
max_allocated_storage = 0

# The interval, in seconds, between points when Enhanced Monitoring metrics
# are collected for the DB instance. To disable collecting Enhanced Monitoring
# metrics, specify 0. Valid Values: 0, 1, 5, 10, 15, 30, 60. Enhanced
# Monitoring metrics are useful when you want to see how different processes
# or threads on a DB instance use the CPU.
monitoring_interval = 0

# The ARN for the IAM role that permits RDS to send enhanced monitoring
# metrics to CloudWatch Logs. If monitoring_interval is greater than 0, but
# monitoring_role_arn is let as an empty string, a default IAM role that
# allows enhanced monitoring will be created.
monitoring_role_arn = null

# Specifies if a standby instance should be deployed in another availability
# zone. If the primary fails, this instance will automatically take over.
multi_az = false

# (Optional) The network type of the DB instance. Valid values: IPV4, DUAL. By
# default, it's set to IPV4.
network_type = null

# The number of read replicas to create. RDS will asynchronously replicate all
# data from the master to these replicas, which you can use to horizontally
# scale reads traffic.
num_read_replicas = 0

# Name of a DB parameter group to associate.
parameter_group_name = null

# Specifies whether Performance Insights are enabled. Performance Insights can
# be enabled for specific versions of database engines. See
# https://aws.amazon.com/rds/performance-insights/ for more details. NOTE: The
# Performance Insights console and flexible retention periods are deprecated
# as of June 30, 2026. Consider using database_insights_mode ('standard' or
# 'advanced') on the primary instance instead.
performance_insights_enabled = false

# The ARN for the KMS key to encrypt Performance Insights data. When
# specifying performance_insights_kms_key_id, performance_insights_enabled
# needs to be set to true. Once KMS key is set, it can never be changed. When
# set to `null` default aws/rds KMS for given region is used.
performance_insights_kms_key_id = null

# The amount of time in days to retain Performance Insights data. Either 7 (7
# days) or 731 (2 years). When specifying
# performance_insights_retention_period, performance_insights_enabled needs to
# be set to true. Defaults to `7`. NOTE: Flexible PI retention periods (paid
# tier) are deprecated as of June 30, 2026.
performance_insights_retention_period = null

# WARNING: - In nearly all cases a database should NOT be publicly accessible.
# Only set this to true if you want the database open to the internet.
publicly_accessible = false

# Identifiers of the replica you want to create. Use this variable if you want
# to set custom identifier for your read replicas.
replica_identifiers = null

# Determines whether a final DB snapshot is created before the DB instance is
# deleted. Be very careful setting this to true; if you do, and you delete
# this DB instance, you will not have any backups of the data!
skip_final_snapshot = false

# Specifies whether the DB instance is encrypted.
storage_encrypted = true

# The type of storage to use for the read replica instance. Must be one of
# 'standard' (magnetic), 'gp2' (general purpose SSD), 'gp3' (general purpose
# SSD), 'io1' (provisioned IOPS SSD), or 'io2' (2nd gen provisioned IOPS SSD).
# AWS recommends 'gp3' for most workloads.
storage_type = "gp2"

# A list of subnet ids where the database should be deployed. In the standard
# Gruntwork VPC setup, these should be the private persistence subnet ids.
# This is ignored if create_subnet_group=false.
subnet_ids = null

# Time zone of the DB instance. timezone is currently only supported by
# Microsoft SQL Server. The timezone can only be set on creation. See MSSQL
# User Guide
# (https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_SQLServer.html#SQLServer.Concepts.General.TimeZone)
# for more information.
timezone = null

# Timeout for DB updating
updating_timeout = "80m"

}


Reference​

Required​

instance_typestringrequired

The instance type to use for the db (e.g. db.t2.micro)

namestringrequired

The name used to namespace all resources created by these templates, including the DB instance (e.g. drupaldb). Must be unique for this region. May contain only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces.

portnumberrequired

The port the DB will listen on (e.g. 3306)

primary_instance_idstringrequired

An ID of the primary DB instance to create read replicas from

vpc_idstringrequired

The id of the VPC in which this DB should be deployed.

Optional​

additional_security_group_idslist(string)optional

List of IDs of AWS Security Groups to attach to the read replica RDS instance.

[]

A list of CIDR-formatted IP address ranges that can connect to read replica instances. If not set read replica instances will use the same security group as master instance.

[]

A list of IPv6 CIDR-formatted IP address ranges that can connect to read replica instances. If not set read replica instances will use the same security group as master instance.

[]

A list of Security Groups that can connect to read replica instances. If not set read replica instances will use the same security group as master instance.

[]

Indicates whether major version upgrades (e.g. 9.4.x to 9.5.x) will ever be permitted. Note that these updates must always be manually performed and will never automatically applied.

true
allowed_replica_zoneslist(string)optional

The availability zones within which it should be possible to spin up replicas

[]
apply_immediatelybooloptional

Specifies whether any cluster modifications are applied immediately, or during the next maintenance window. Note that cluster modifications may cause degraded performance or downtime.

false

Indicates that minor engine upgrades will be applied automatically to the DB instance during the maintenance window. If set to true, you should set engine_version to MAJOR.MINOR and omit the .PATCH at the end (e.g., use 5.7 and not 5.7.11); otherwise, you'll get Terraform state drift. See https://www.terraform.io/docs/providers/aws/r/db_instance.html#engine_version for more details.

true

The description of the aws_db_subnet_group that is created. Defaults to 'Subnet group for the name DB' if not specified.

null

The name of the aws_db_subnet_group that is created, or an existing one to use if create_subnet_group is false. Defaults to name if not specified.

null

How many days to keep backup snapshots around before cleaning them up. Must be 1 or greater to support read replicas. 0 means disable automated backups.

21
backup_windowstringoptional

The daily time range during which automated backups are created (e.g. 04:00-09:00). Time zone is UTC. Performance may be degraded while a backup runs.

null
ca_cert_identifierstringoptional

The Certificate Authority (CA) certificates bundle to use on the RDS instance.

null

Copy all the RDS instance tags to snapshots. Default is false.

false

When working with read replicas, only configure db subnet group if the source database specifies an instance in another AWS Region. If true, it will create a new subnet group.

false
creating_timeoutstringoptional

Timeout for DB creating

"40m"
custom_tagsmap(string)optional

A map of custom tags to apply to the RDS Instance and the Security Group created for it. The key is the tag name and the value is the tag value.

{}

Specifies whether to remove automated backups immediately after the DB instance is deleted

null
deleting_timeoutstringoptional

Timeout for DB deleting

"60m"

The database can't be deleted when this value is set to true. The default is false.

false

List of log types to enable for exporting to CloudWatch logs. If omitted, no logs will be exported. Valid values (depending on engine): alert, audit, error, general, listener, slowquery, trace, postgresql (PostgreSQL) and upgrade (PostgreSQL).

[]
engine_versionstringoptional

The engine version to run on the read replicas (e.g. 16.14 for postgres). If null, each replica reports whatever version AWS gives it, which is the version it inherited from the primary. Set this to upgrade existing replicas ahead of the source instance, which AWS requires for a minor version upgrade on any engine, and on MySQL and MariaDB for a major version upgrade too. Three caveats: it has no effect when a replica is created, since a new replica always comes up on the source instance version and a second apply moves it; the change is held until the maintenance window unless apply_immediately is true; and while auto_minor_version_upgrade is true, prefer MAJOR.MINOR over a full patch version and clear this back to null once the upgrade is done, or AWS patching will drift away from the pinned value. Leave it null for a PostgreSQL major version upgrade of an in-Region replica: AWS upgrades those along with the primary. See this module README for the sequence.

null

Specifies whether IAM database authentication is enabled. This option is only available for MySQL and PostgreSQL engines.

null
iopsnumberoptional

The amount of provisioned IOPS for the primary instance. Setting this implies a storage_type of 'io1','io2, or 'gp3'. Set to 0 to disable.

0
kms_key_arnstringoptional

The ARN of a KMS key that should be used to encrypt data on disk. Only used if storage_encrypted is true. If you leave this blank, the default RDS KMS key for the account will be used.

null
maintenance_windowstringoptional

The weekly day and time range during which system maintenance can occur (e.g. wed:04:00-wed:04:30). Time zone is UTC. Performance may be degraded or there may even be a downtime during maintenance windows.

null
max_allocated_storagenumberoptional

When configured, the upper limit to which Amazon RDS can automatically scale the storage of the DB instance. Configuring this will automatically ignore differences to allocated_storage. Must be greater than or equal to allocated_storage or 0 to disable Storage Autoscaling.

0
monitoring_intervalnumberoptional

The interval, in seconds, between points when Enhanced Monitoring metrics are collected for the DB instance. To disable collecting Enhanced Monitoring metrics, specify 0. Valid Values: 0, 1, 5, 10, 15, 30, 60. Enhanced Monitoring metrics are useful when you want to see how different processes or threads on a DB instance use the CPU.

0
monitoring_role_arnstringoptional

The ARN for the IAM role that permits RDS to send enhanced monitoring metrics to CloudWatch Logs. If monitoring_interval is greater than 0, but monitoring_role_arn is let as an empty string, a default IAM role that allows enhanced monitoring will be created.

null
multi_azbooloptional

Specifies if a standby instance should be deployed in another availability zone. If the primary fails, this instance will automatically take over.

false
network_typestringoptional

(Optional) The network type of the DB instance. Valid values: IPV4, DUAL. By default, it's set to IPV4.

null
num_read_replicasnumberoptional

The number of read replicas to create. RDS will asynchronously replicate all data from the master to these replicas, which you can use to horizontally scale reads traffic.

0
parameter_group_namestringoptional

Name of a DB parameter group to associate.

null

Specifies whether Performance Insights are enabled. Performance Insights can be enabled for specific versions of database engines. See https://aws.amazon.com/rds/performance-insights/ for more details. NOTE: The Performance Insights console and flexible retention periods are deprecated as of June 30, 2026. Consider using database_insights_mode ('standard' or 'advanced') on the primary instance instead.

false

The ARN for the KMS key to encrypt Performance Insights data. When specifying performance_insights_kms_key_id, performance_insights_enabled needs to be set to true. Once KMS key is set, it can never be changed. When set to null default aws/rds KMS for given region is used.

null

The amount of time in days to retain Performance Insights data. Either 7 (7 days) or 731 (2 years). When specifying performance_insights_retention_period, performance_insights_enabled needs to be set to true. Defaults to 7. NOTE: Flexible PI retention periods (paid tier) are deprecated as of June 30, 2026.

null

WARNING: - In nearly all cases a database should NOT be publicly accessible. Only set this to true if you want the database open to the internet.

false
replica_identifierslist(string)optional

Identifiers of the replica you want to create. Use this variable if you want to set custom identifier for your read replicas.

null

Determines whether a final DB snapshot is created before the DB instance is deleted. Be very careful setting this to true; if you do, and you delete this DB instance, you will not have any backups of the data!

false
storage_encryptedbooloptional

Specifies whether the DB instance is encrypted.

true
storage_typestringoptional

The type of storage to use for the read replica instance. Must be one of 'standard' (magnetic), 'gp2' (general purpose SSD), 'gp3' (general purpose SSD), 'io1' (provisioned IOPS SSD), or 'io2' (2nd gen provisioned IOPS SSD). AWS recommends 'gp3' for most workloads.

"gp2"
subnet_idslist(string)optional

A list of subnet ids where the database should be deployed. In the standard Gruntwork VPC setup, these should be the private persistence subnet ids. This is ignored if create_subnet_group=false.

null
timezonestringoptional

Time zone of the DB instance. timezone is currently only supported by Microsoft SQL Server. The timezone can only be set on creation. See MSSQL User Guide (https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_SQLServer.html#SQLServer.Concepts.General.TimeZone) for more information.

null
updating_timeoutstringoptional

Timeout for DB updating

"80m"