This check flags RDS instances that aren't exporting any database logs to CloudWatch, which leaves you blind during outages, breaches, and slow-query investigations. Fix it by enabling log exports on the instance, either in the console, via the CLI --cloudwatch-logs-export-configuration flag, or in your Terraform with enabled_cloudwatch_logs_exports.
Your RDS instance is happily writing logs the whole time it runs. Error logs, slow query logs, audit logs, and general logs all get generated by the database engine. The catch is that by default those logs stay local to the instance. They rotate, they age out, and when the instance restarts or gets replaced, they're gone. The RDS Has No CloudWatch Log Exports check catches the case where none of those logs are being shipped to CloudWatch Logs, so you have no durable, queryable record of what your database was doing.
What this check detects
The check inspects each RDS instance and looks at its EnabledCloudwatchLogsExports configuration. If the list is empty, the instance is flagged. In plain terms, the engine is producing logs but nothing is leaving the database host.
The specific log types available depend on the engine:
- MySQL / MariaDB:
audit,error,general,slowquery - PostgreSQL:
postgresql,upgrade - Oracle:
alert,audit,listener,trace - SQL Server:
agent,error
Note: This is different from RDS performance metrics. CloudWatch already collects metrics like CPU and connections automatically. Log exports are a separate opt-in feature that streams the actual text logs from the engine into CloudWatch Logs groups named like /aws/rds/instance/<name>/error.
Why it matters
Logs that never leave the instance are logs you can't use when it counts. Here are the situations where this gap bites.
Incident investigation goes cold
If an instance fails over, gets replaced during a maintenance event, or you delete it during cleanup, any local logs disappear with it. When you later need to understand a 2 AM outage or a data integrity issue, the evidence is gone. CloudWatch Logs gives you a durable copy that survives the instance.
Security and audit blind spots
Audit logs capture who connected, what queries ran, and failed authentication attempts. Without exporting them, you can't detect a credential-stuffing attempt against your database, a sudden burst of failed logins, or an unexpected admin connecting from an odd source. For a compromised app server pivoting to the database, the audit log is often the only artifact that records the activity.
Compliance failures
PCI DSS, HIPAA, SOC 2, and most other frameworks require centralized, tamper-resistant log retention. Logs sitting on an ephemeral RDS host don't satisfy that. Auditors want to see logs collected in a central store with a defined retention period, which CloudWatch Logs provides.
Performance debugging without the data
Slow query logs are the fastest way to find the query dragging down your database. If they aren't being exported and the instance restarted since the incident, you're reconstructing the problem from memory instead of reading it directly.
Warning: CloudWatch Logs ingestion and storage are billed. High-traffic databases with the general log enabled can generate large volumes quickly. Enable the log types you actually need (error and audit at minimum, slowquery for tuning) rather than turning on everything blindly, and set a retention policy so old logs expire.
How to fix it
Console
- Open the RDS console and select your database instance.
- Click Modify.
- Scroll to the Log exports section.
- Check the log types you want to export (for example Error log, Slow query log, Audit log).
- Click Continue, choose to apply immediately or during the next maintenance window, then Modify DB instance.
AWS CLI
For a MySQL or MariaDB instance, enable error, slow query, and audit logs:
aws rds modify-db-instance \
--db-instance-identifier my-prod-db \
--cloudwatch-logs-export-configuration '{"EnableLogTypes":["error","slowquery","audit"]}' \
--apply-immediately
For PostgreSQL:
aws rds modify-db-instance \
--db-instance-identifier my-postgres-db \
--cloudwatch-logs-export-configuration '{"EnableLogTypes":["postgresql","upgrade"]}' \
--apply-immediately
Warning: Enabling log exports modifies the instance. Most engines apply this without a reboot, but for some log types (notably MySQL audit when the audit plugin needs activation through a parameter group) you may need a reboot. Test in staging first, and avoid --apply-immediately on sensitive production databases unless you've confirmed there's no restart involved.
Some logs need a parameter group setting before the engine actually writes them. For MySQL slow query logging, make sure slow_query_log is on and the log destination is set to file:
aws rds modify-db-parameter-group \
--db-parameter-group-name my-mysql-params \
--parameters \
"ParameterName=slow_query_log,ParameterValue=1,ApplyMethod=immediate" \
"ParameterName=log_output,ParameterValue=FILE,ApplyMethod=immediate" \
"ParameterName=long_query_time,ParameterValue=2,ApplyMethod=immediate"
Terraform
Set the log types directly on the resource and define the parameter group settings the engine needs:
resource "aws_db_instance" "prod" {
identifier = "my-prod-db"
engine = "mysql"
instance_class = "db.t3.medium"
# ... other config ...
enabled_cloudwatch_logs_exports = ["error", "slowquery", "audit"]
parameter_group_name = aws_db_parameter_group.prod.name
}
resource "aws_db_parameter_group" "prod" {
name = "my-mysql-params"
family = "mysql8.0"
parameter {
name = "slow_query_log"
value = "1"
}
parameter {
name = "log_output"
value = "FILE"
}
}
Set retention on the resulting log groups
RDS creates the CloudWatch log groups with no expiration by default, so they accumulate forever. Set a retention period:
aws logs put-retention-policy \
--log-group-name /aws/rds/instance/my-prod-db/error \
--retention-in-days 90
Tip: If you manage log groups in Terraform with aws_cloudwatch_log_group, set retention_in_days there and reference the same name RDS uses. That keeps retention under version control instead of being a manual one-off that drifts.
How to prevent it from happening again
Fixing one instance is easy. Keeping every future instance compliant is the real win. Bake log exports into the way databases get created.
Make it a module default
If your team provisions RDS through a shared Terraform module, set enabled_cloudwatch_logs_exports as a sensible default in the module rather than leaving it to each caller. Most teams forget optional flags, so the default decides the outcome.
Gate it in CI/CD with policy-as-code
Use a tool like Checkov, tfsec, or OPA/Conftest to fail any plan that creates an RDS instance without log exports. A Conftest rule looks like this:
package main
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_db_instance"
exports := resource.change.after.enabled_cloudwatch_logs_exports
count(exports) == 0
msg := sprintf("RDS instance '%s' has no CloudWatch log exports enabled", [resource.address])
}
Wire that into your pipeline so a pull request that introduces a non-compliant database never merges.
Enforce with an SCP or Config rule
For environments where you can't trust every pipeline, AWS Config can continuously evaluate live instances. The managed rule rds-logging-enabled flags any RDS instance not exporting the expected log types, and you can pair it with an automatic remediation action.
Tip: Lensix runs this check continuously across your accounts, so you catch instances that were created out-of-band or by a teammate who clicked through the console. That covers the gap your CI pipeline can't see.
Best practices
- Always export error and audit logs. These are the highest-value, lowest-volume logs. Error logs explain failures, audit logs explain access. Enable them everywhere.
- Enable slow query logs in environments where you tune performance. Set a reasonable
long_query_timeso you capture the genuinely slow queries without drowning in noise. - Be deliberate with the general log. It records every statement and grows fast. Use it for short debugging windows, not as a permanent setting.
- Always set retention. Unbounded log groups quietly run up your bill. Match retention to your compliance requirement, often 90 days hot with archival to S3 beyond that.
- Ship logs onward. Use a CloudWatch Logs subscription filter to forward audit and error logs to your SIEM so security teams see database activity alongside everything else.
- Apply the same standard to Aurora. Aurora clusters use
enabled_cloudwatch_logs_exportson the cluster resource, and the same reasoning applies.
Note: Exporting logs to CloudWatch and forwarding them to a SIEM are complementary, not redundant. CloudWatch gives you queryable storage and retention. The SIEM gives security correlation and alerting. The export is the foundation both depend on.
Database logs are cheap insurance. The cost of enabling a couple of log types is small and predictable. The cost of not having them is an investigation you can't complete, an audit finding you can't close, or a breach you can't reconstruct. Turn them on, set retention, and enforce it in your pipeline so it stays on.

