Skip to content

RDS for PostgreSQL DB instances should publish logs to CloudWatch Logs

PostgreSQL logs contain connection attempts, query errors, deadlocks, and authentication failures. Without forwarding these to CloudWatch Logs, you lose centralized visibility into database activity. Incident responders cannot correlate application errors with database events, and retention depends entirely on the RDS instance lifecycle rather than a durable log store.

Exporting to CloudWatch also unlocks metric filters, alarms, and CloudWatch Logs Insights for ad-hoc investigation. If the instance is terminated or storage fills up, local logs may be lost permanently.

Retrofit consideration

Enabling CloudWatch log exports on an existing RDS instance triggers a modification. Depending on your apply_immediately setting, this may cause a brief connectivity interruption during the change window.

Implementation

Choose the approach that matches how you manage Terraform.

If you use terraform-aws-modules/rds/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "rds" {
  source  = "terraform-aws-modules/rds/aws"
  version = ">=7.0.0"

  allocated_storage      = 20
  db_name                = "myapp"
  db_subnet_group_name   = "example-db-subnet-group"
  engine                 = "mysql"
  engine_version         = "8.0.41"
  family                 = "mysql8.0"
  identifier             = "abc123"
  instance_class         = "db.t3.micro"
  major_engine_version   = "8.0"
  password_wo            = "change-me-in-production"
  skip_final_snapshot    = true
  username               = "dbadmin"
  vpc_security_group_ids = ["sg-12345678"]
}

Use AWS provider resources directly. See docs for the resources involved: aws_db_instance.

resource "aws_db_instance" "this" {
  allocated_storage               = 20
  enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
  engine                          = "postgres"
  identifier                      = "pofix-abc123"
  instance_class                  = "db.t3.micro"
  monitoring_interval             = 60
  monitoring_role_arn             = "arn:aws:iam::123456789012:role/example-role"
  password                        = "ChangeMe123!"
  skip_final_snapshot             = true
  username                        = "dbadmin"
}

What this control checks

For aws_db_instance resources with engine = "postgres", enabled_cloudwatch_logs_exports must include the required log types. At minimum, "postgresql" must be present; "upgrade" may also be required depending on the control's logTypes parameter. A passing configuration looks like enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]. It fails if the argument is omitted, set to an empty list, or missing one of the expected values. No additional IAM configuration is required: RDS automatically creates the necessary CloudWatch Logs log groups and streams when exports are enabled.

Common pitfalls

  • Omitting upgrade log type

    If the control's logTypes parameter expects both "postgresql" and "upgrade", including only "postgresql" still triggers a failure. Check the parameterization before assuming the default list is sufficient.

  • Engine mismatch with Aurora PostgreSQL

    This control targets aws_db_instance resources with engine = "postgres", not Aurora PostgreSQL clusters. Aurora PostgreSQL logging is configured on aws_rds_cluster with enabled_cloudwatch_logs_exports, which is a separate control surface evaluated independently.

  • Insufficient log_min_messages parameter

    Enabling log export does not control what PostgreSQL actually logs. If log_statement, log_min_duration_statement, or log_connections are unset in the associated aws_db_parameter_group, the logs flowing into CloudWatch may contain very little useful signal.

  • CloudWatch Logs retention defaults to never expire

    Declare the log group explicitly in Terraform using aws_cloudwatch_log_group and set retention_in_days before enabling exports. RDS reuses an existing log group rather than creating a new one, so without an explicit resource the group defaults to indefinite retention and storage costs grow unbounded.

Audit evidence

AWS Config evaluation results showing all RDS PostgreSQL instances as compliant for log export configuration are the primary evidence. Supporting that, the CloudWatch Logs console should show active log groups under /aws/rds/instance/<instance-id>/postgresql with recent log streams confirming logs are flowing. The RDS console's "Configuration" tab for each instance lists the published log types under "Log exports."

For deeper assurance, CloudTrail ModifyDBInstance events confirm when log exports were enabled. CloudWatch Logs retention policies on the relevant log groups demonstrate the organization is retaining logs for the required duration.

Framework-specific interpretation

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: rds_db_instance_postgres_logging_enabled

  • AWS Config Managed Rule: RDS_POSTGRESQL_LOGS_TO_CLOUDWATCH

  • Checkov Check: CKV_AWS_129

  • Powerpipe Control: aws_compliance.control.rds_db_instance_postgres_logging_enabled

  • Prowler Check: rds_instance_integration_cloudwatch_logs

  • AWS Security Hub Controls: RDS.36, RDS.9

Last reviewed: 2026-03-09