Terraform’s aws_db_parameter_group resource gives infrastructure engineers direct control over engine-level tuning for Amazon RDS without leaving HCL. Parameter groups decouple database configuration from instance provisioning, allowing reusable sets of name-value pairs that can be attached to one or more DB instances. The resource maps closely to the AWS RDS DB parameter group API, with support for family selection, per-parameter apply methods, and lifecycle behaviors that matter during major version upgrades.
How Parameter Groups Fit Into an RDS Deployment
A typical RDS deployment combines a subnet group, security group, parameter group, and the DB instance itself. The subnet group resource is an optional parameter in the aws_db_instance block. Without it, Terraform creates RDS instances in the default VPC.
hcl
resource "aws_db_subnet_group" "education" {
name = "education"
subnet_ids = module.vpc.public_subnets
tags = {
Name = "Education"
}
}
The instance block references the subnet group, security group, and parameter group by name:
hcl
resource "aws_db_instance" "education" {
identifier = "education"
instance_class = "db.t3.micro"
allocated_storage = 5
engine = "postgres"
engine_version = "18.3"
username = "edu"
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.education.name
vpc_security_group_ids = [aws_security_group.rds.id]
parameter_group_name = aws_db_parameter_group.education.name
publicly_accessible = true
skip_final_snapshot = true
}
Note the following arguments. username and password are the credentials for the root user. publicly_accessible is set to true for this tutorial’s configuration. Attaching a custom parameter group lets you override engine defaults for character sets, memory allocation, logging, and durability settings while keeping the instance definition clean.
Resource Definition and Core Arguments
The aws_db_parameter_group resource provides an RDS DB parameter group resource. Documentation of available parameters for various RDS engines can be found at Aurora MySQL Parameters, Aurora PostgreSQL Parameters, MariaDB Parameters, Oracle Parameters, PostgreSQL Parameters.
A minimal definition requires a name and a family:
hcl
resource "aws_db_parameter_group" "default" {
name = "rds-pg"
family = "mysql5.6"
parameter {
name = "character_set_server"
value = "utf8"
}
parameter {
name = "character_set_client"
value = "utf8"
}
}
Argument reference for the resource includes:
| Argument | Required | Description |
|---|---|---|
| name | Required | The name of the DB parameter group. Optional Forces new resource. |
| family | Required | The family of the DB parameter group. Must match engine and major version. |
| description | Optional | The description of the DB parameter group. Defaults to “Managed by Terraform”. |
| parameter | Optional | A list of DB parameters to apply. |
| tags | Optional | A mapping of tags to assign to the resource. |
| region | Optional | Region where this resource will be managed. Defaults to the Region set in the provider configuration. |
Parameter blocks support:
| Argument | Required | Description |
|---|---|---|
| name | Required | The name of the DB parameter. |
| value | Required | The value of the DB parameter. |
| apply_method | Optional | “immediate” (default), or “pending-reboot”. Some engines can’t apply some parameters without a reboot, and you will need to specify “pending-reboot” here. |
Attributes exported by the resource are:
| Attribute | Description |
|---|---|
| id | The db parameter group name. |
| arn | The ARN of the db parameter group. |
Import is supported using the name:
bash
$ terraform import aws_db_parameter_group.rds_pg rds-pg
Families and Engine Mapping
The family must match your engine and major version. Common families include:
mysql8.0for MySQL 8.0.xpostgres16for PostgreSQL 16.xmariadb10.11for MariaDB 10.11.xaurora-mysql8.0for Aurora MySQL 8.0aurora-postgresql16for Aurora PostgreSQL 16
Choosing the correct family is critical. A mismatch prevents the parameter group from being attached and will cause perpetual diffs. Terraform plan showing parameter changes after an apply, i.e., perpetual diffs, can occur when the family or parameter set drifts from AWS defaults.
Apply Methods: Dynamic vs Static Parameters
Dynamic parameters take effect immediately when changed. Static parameters require a reboot of the instance before they take effect. This distinction matters a lot in Terraform.
Example for PostgreSQL 16 with mixed apply methods:
hcl
resource "aws_db_parameter_group" "postgres" {
name = "myapp-postgres16"
family = "postgres16"
description = "Custom PostgreSQL 16 parameters for myapp"
parameter {
name = "max_connections"
value = "200"
apply_method = "pending-reboot"
}
parameter {
name = "shared_buffers"
value = "{DBInstanceClassMemory/4}"
apply_method = "pending-reboot"
}
parameter {
name = "log_min_duration_statement"
value = "1000"
}
tags = {
Name = "myapp-postgres16"
Environment = "production"
}
}
Formula values such as {DBInstanceClassMemory/4} are evaluated by RDS at instance creation, allowing memory-proportional tuning without hardcoding instance class specifics.
For MySQL 8.0 a production-ready tuning set demonstrates character set, InnoDB, and query tuning:
hcl
resource "aws_db_parameter_group" "mysql" {
name_prefix = "myapp-mysql80-"
family = "mysql8.0"
description = "Tuned MySQL 8.0 parameters"
parameter {
name = "character_set_server"
value = "utf8mb4"
}
parameter {
name = "collation_server"
value = "utf8mb4_0900_ai_ci"
}
parameter {
name = "innodb_buffer_pool_size"
value = "{DBInstanceClassMemory*3/4}"
}
parameter {
name = "innodb_buffer_pool_instances"
value = "8"
apply_method = "pending-reboot"
}
parameter {
name = "innodb_redo_log_capacity"
value = "2147483648"
}
parameter {
name = "innodb_flush_log_at_trx_commit"
value = "1"
}
parameter {
name = "innodb_io_capacity"
value = "3000"
}
parameter {
name = "join_buffer_size"
value = "262144"
}
parameter {
name = "sort_buffer_size"
value = "524288"
}
parameter {
name = "slow_query_log"
value = "1"
}
}
name_prefix causes Terraform to create a new parameter group with a unique suffix, update the instance if its parameter_group_name references the parameter group resource, and then delete the old one after it is no longer attached. This is useful for immutable naming strategies.
Perpetual Diff Avoidance and Default Management
If you encounter a Terraform plan showing parameter changes after an apply, see the Problematic Plan Changes example guidance. The most common cause is Terraform managing a parameter that AWS manages by default.
One approach is to omit the parameter entirely. This ensures Terraform does not attempt to modify the parameter, leaving it with AWS's default settings.
hcl
resource "aws_db_parameter_group" "test" {
name = "random-test-parameter"
family = "mysql5.7"
}
Another approach is to explicitly set the value and apply_method to match AWS defaults. Since the AWS default value is 0, selecting any other valid value e.g., 1 will resolve the issue.
hcl
resource "aws_db_parameter_group" "test" {
name = "random-test-parameter"
family = "mysql5.7"
parameter {
name = "default_password_lifetime"
value = "1"
}
}
When the AWS default value is 0 and the parameter uses pending-reboot by default, you must align both value and apply_method to avoid conflicts.
hcl
resource "aws_db_parameter_group" "test" {
name = "random-test-parameter"
family = "mysql5.7"
parameter {
apply_method = "pending-reboot"
name = "default_password_lifetime"
value = "0"
}
}
Explicitly set the apply_method to match AWS's default value for this parameter pending-reboot. This prevents conflicts between Terraform's default immediate and AWS's default where the value is not changing.
Lifecycle and Recreation Behavior
The create_before_destroy lifecycle configuration is necessary for modifications that force re-creation of an existing, in-use parameter group. This includes common situations like changing the group name or bumping the family version during a major version upgrade. This configuration will prevent destruction of the deposed parameter group while still in use by the database during upgrade.
Note: Using create_before_destroy requires that the new parameter group is created with a different name than the existing one.
hcl
resource "aws_db_parameter_group" "default" {
name = "rds-pg"
family = "mysql5.6"
lifecycle {
create_before_destroy = true
}
}
This pattern is essential when upgrading families, for example from mysql5.6 to mysql5.7, because RDS requires a new parameter group and a reboot window.
Hands-On Workflow and Verification
After applying a parameter group change, Terraform shows a plan summary and then applies:
bash
$ terraform apply
aws_db_parameter_group.education: Refreshing state... [id=education]
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-03d07a04a25ae3f80]
Plan: 1 to add, 1 to change, 0 to destroy.
Once complete, outputs can be used to connect and verify:
Outputs:
rds_hostname = <sensitive>
rds_port = <sensitive>
rds_replica_connection_parameters = "-h education-replica.cyfmek5yt2i5.us-east-2.rds.amazonaws.com -p 5432 -U edu postgres"
rds_username = <sensitive>
As with the original instance, it may take 5-7 minutes to provision the replica, and a few additional minutes to make updates to the primary instance. Once it is complete, use the new endpoint to connect to the replica database instance to verify your configuration.
Common Pitfalls
- Using a family that does not match the engine version leads to API validation errors.
- Setting
apply_method = "immediate"on a static parameter causes Terraform to wait indefinitely for an effective change that requires reboot. - Forgetting to attach the parameter group via
parameter_group_nameonaws_db_instanceresults in the group existing but unused. - Changing the name of an in-use parameter group without
create_before_destroycauses downtime.
Conclusion
The aws_db_parameter_group resource is the mechanism for codifying engine tuning, compliance, and observability settings for RDS. Success depends on precise family selection, correct applymethod for dynamic versus static parameters, and lifecycle handling for upgrades. By pairing parameter groups with subnet groups, security groups, and instance definitions, Terraform can reproduce RDS environments consistently across development, staging, and production. The key operational practices are to avoid managing parameters that AWS defaults manage, to explicitly set applymethod where AWS defaults differ from Terraform defaults, and to use create_before_destroy when changing group identity or family during major version upgrades. These patterns eliminate perpetual diffs, prevent accidental reboots, and keep infrastructure as code aligned with RDS behavior.