Skip to main content
Aurora PostgreSQL is a PostgreSQL-compatible relational database engine built for the cloud. It uses the engine identifier aurora-postgresql and supports PostgreSQL-compatible versions.

Complete example

The following is the full working example from examples/postgresql/. It creates a three-instance cluster with Aurora I/O Optimized storage, cluster-level enhanced monitoring, per-instance overrides, custom endpoints, a cluster parameter group, a DB parameter group, PostgreSQL log exports, and a KMS-encrypted activity stream.
provider "aws" {
  region = local.region
}

data "aws_availability_zones" "available" {
  # Exclude local zones
  filter {
    name   = "opt-in-status"
    values = ["opt-in-not-required"]
  }
}

locals {
  name   = "ex-${basename(path.cwd)}"
  region = "eu-west-1"

  vpc_cidr = "10.0.0.0/16"
  azs      = slice(data.aws_availability_zones.available.names, 0, 3)

  tags = {
    Example    = local.name
    GithubRepo = "terraform-aws-rds-aurora"
    GithubOrg  = "terraform-aws-modules"
  }
}

module "aurora" {
  source = "terraform-aws-modules/rds-aurora/aws"

  name                        = local.name
  engine                      = "aurora-postgresql"
  engine_version              = "17.5"
  master_username             = "root"
  storage_type                = "aurora-iopt1"
  cluster_monitoring_interval = 30

  instances = {
    1 = {
      instance_class          = "db.r8g.2xlarge"
      publicly_accessible     = true
      db_parameter_group_name = "default.aurora-postgresql14"
    }
    2 = {
      identifier     = "static-member-1"
      instance_class = "db.r8g.2xlarge"
    }
    3 = {
      identifier     = "excluded-member-1"
      instance_class = "db.r8g.large"
      promotion_tier = 15
    }
  }

  endpoints = {
    static = {
      identifier     = "static-custom-endpt"
      type           = "ANY"
      static_members = ["static-member-1"]
      tags           = { Endpoint = "static-members" }
    }
    excluded = {
      identifier       = "excluded-custom-endpt"
      type             = "READER"
      excluded_members = ["excluded-member-1"]
      tags             = { Endpoint = "excluded-members" }
    }
  }

  vpc_id               = module.vpc.vpc_id
  db_subnet_group_name = module.vpc.database_subnet_group_name
  security_group_ingress_rules = {
    private-az1 = {
      cidr_ipv4 = element(module.vpc.private_subnets_cidr_blocks, 0)
    }
    private-az2 = {
      cidr_ipv4 = element(module.vpc.private_subnets_cidr_blocks, 1)
    }
    private-az3 = {
      cidr_ipv4 = element(module.vpc.private_subnets_cidr_blocks, 2)
    }
  }

  apply_immediately   = true
  skip_final_snapshot = true

  engine_lifecycle_support = "open-source-rds-extended-support-disabled"

  cluster_parameter_group = {
    name        = local.name
    family      = "aurora-postgresql17"
    description = "${local.name} example cluster parameter group"
    parameters = [
      {
        name         = "log_min_duration_statement"
        value        = 4000
        apply_method = "immediate"
      },
      {
        name         = "rds.force_ssl"
        value        = 1
        apply_method = "pending-reboot"
      }
    ]
  }

  db_parameter_group = {
    name        = local.name
    family      = "aurora-postgresql17"
    description = "${local.name} example DB parameter group"
    parameters = [
      {
        name         = "log_min_duration_statement"
        value        = 4000
        apply_method = "immediate"
      }
    ]
  }

  enabled_cloudwatch_logs_exports = ["postgresql"]
  create_cloudwatch_log_group     = true

  cluster_activity_stream = {
    kms_key_id = module.kms.key_id
    mode       = "async"
  }

  tags = local.tags
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 6.0"

  name = local.name
  cidr = local.vpc_cidr

  azs              = local.azs
  public_subnets   = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)]
  private_subnets  = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 3)]
  database_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 6)]

  tags = local.tags
}

module "kms" {
  source  = "terraform-aws-modules/kms/aws"
  version = "~> 4.0"

  deletion_window_in_days = 7
  description             = "KMS key for ${local.name} cluster activity stream"
  enable_key_rotation     = true
  is_enabled              = true
  key_usage               = "ENCRYPT_DECRYPT"

  aliases = ["rds/${local.name}"]

  tags = local.tags
}

Instance configuration

Each key in the instances map creates one aws_rds_cluster_instance. The cluster_instance_class sets the default class; individual instances can override it and set other per-instance attributes. Homogeneous cluster — every instance uses the same class:
cluster_instance_class = "db.r8g.large"
instances = {
  one   = {}
  two   = {}
  three = {}
}
Per-instance overrides — mix classes, identifiers, promotion tiers, and parameter groups:
instances = {
  1 = {
    instance_class          = "db.r8g.2xlarge"
    publicly_accessible     = true
    db_parameter_group_name = "default.aurora-postgresql14"
  }
  2 = {
    identifier     = "static-member-1"
    instance_class = "db.r8g.2xlarge"
  }
  3 = {
    identifier     = "excluded-member-1"
    instance_class = "db.r8g.large"
    promotion_tier = 15
  }
}
Set promotion_tier = 15 on instances you want to exclude from failover promotion. Combined with custom endpoints, this lets you route specific workloads to specific readers.

Custom endpoints

The endpoints map creates additional aws_rds_cluster_endpoint resources. Each endpoint can target a static list of members or exclude specific members.
endpoints = {
  static = {
    identifier     = "static-custom-endpt"
    type           = "ANY"
    static_members = ["static-member-1"]
    tags           = { Endpoint = "static-members" }
  }
  excluded = {
    identifier       = "excluded-custom-endpt"
    type             = "READER"
    excluded_members = ["excluded-member-1"]
    tags             = { Endpoint = "excluded-members" }
  }
}

Key PostgreSQL-specific variables

VariableTypeDescription
enginestringMust be "aurora-postgresql"
engine_versionstringPostgreSQL-compatible version, e.g. "17.5"
storage_typestring"aurora" (default) or "aurora-iopt1" for I/O Optimized
cluster_monitoring_intervalnumberCluster-level enhanced monitoring interval in seconds. Valid values: 0, 1, 5, 10, 15, 30, 60
enabled_cloudwatch_logs_exportslist(string)Use ["postgresql"] for the PostgreSQL log
engine_lifecycle_supportstringSet to "open-source-rds-extended-support-disabled" to opt out of extended support charges
cluster_parameter_groupobjectInline cluster parameter group. Use family aurora-postgresql17 for PostgreSQL 17.
db_parameter_groupobjectInline DB instance parameter group
endpointsmap(object)Additional custom cluster endpoints

PostgreSQL log exports

Aurora PostgreSQL exports a single log type: postgresql. Enable it and let the module manage the CloudWatch log group:
enabled_cloudwatch_logs_exports = ["postgresql"]
create_cloudwatch_log_group     = true
To customise log retention, set cloudwatch_log_group_retention_in_days (default: 7).

Deployment workflow

1

Prepare networking

Create a VPC with private subnets and a database subnet group spanning at least two availability zones. The module creates a security group automatically when create_security_group = true (the default); provide security_group_ingress_rules to allow inbound traffic on the PostgreSQL port (5432).
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 6.0"

  name             = "my-vpc"
  cidr             = "10.0.0.0/16"
  azs              = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
  private_subnets  = ["10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24"]
  database_subnets = ["10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24"]
}
2

Choose a storage type

For read/write-heavy workloads with unpredictable I/O, set storage_type = "aurora-iopt1" to use Aurora I/O Optimized storage. For standard workloads, omit this variable or set it to "aurora".
3

Define instances

Add at least one entry to instances. The first instance Terraform provisions becomes the writer. Use promotion_tier to control failover order and db_parameter_group_name for per-instance parameter tuning.
4

Configure parameter groups

Use cluster_parameter_group and db_parameter_group to manage parameter groups inline. Match the family to your engine version — aurora-postgresql17 for PostgreSQL 17.
cluster_parameter_group = {
  name   = "my-cluster-pg"
  family = "aurora-postgresql17"
  parameters = [
    {
      name         = "rds.force_ssl"
      value        = 1
      apply_method = "pending-reboot"
    }
  ]
}
5

Enable log exports

enabled_cloudwatch_logs_exports = ["postgresql"]
create_cloudwatch_log_group     = true
6

Apply

terraform init
terraform plan
terraform apply
The cluster_endpoint output contains the writer endpoint. The cluster_reader_endpoint output contains the load-balanced reader endpoint. Any custom endpoints are in additional_cluster_endpoints.

Build docs developers (and LLMs) love