How Aurora MySQL Binlog Storage Works
Aurora MySQL's storage model differs from standard RDS MySQL in one important way: Aurora's shared storage layer handles replication internally across the six storage nodes in three AZs. You do not need binary logging enabled for Aurora reader replicas to stay in sync — they use Aurora's native storage replication, not binlog-based replication.
Binary logging on Aurora MySQL serves a different purpose: external replication consumers. This includes AWS DMS tasks using CDC, Debezium connectors, external MySQL replicas outside of Aurora, read replicas in different regions before Global Database was available, and any application consuming the binlog stream directly via the MySQL replication protocol.
When you enable binlog on Aurora MySQL (by setting binlog_format in a custom parameter group), Aurora starts retaining binlog files. The retention window is controlled by binlog retention hours, set via the mysql.rds_set_configuration procedure. The default is 24 hours when the parameter is not explicitly set — but "default" here means "whatever AWS has configured," and it has changed across Aurora versions and regions. Do not assume it matches what you expect.
-- Check current binlog retention setting
CALL mysql.rds_show_configuration;
-- Output includes:
-- name | value | description
-- binlog retention hours | 24 | ...
-- Also check total binlog storage consumption
SHOW BINARY LOGS;
-- Lists all retained binlog files with their sizes
-- Sum of retained binlog storage
SELECT
COUNT(*) AS binlog_file_count,
ROUND(SUM(File_size) / 1024 / 1024 / 1024, 2) AS total_gb
FROM information_schema.FILES
WHERE FILE_TYPE = 'UNDO LOG';
-- Note: use SHOW BINARY LOGS for accurate binlog sizes
Calculating Your Binlog Storage Cost
Aurora storage is billed at $0.10 per GB-month (pricing varies by region; verify current rates). Binlog storage counts against your Aurora storage consumption the same as table data and index storage.
To estimate your binlog storage cost:
-- Step 1: measure your binlog generation rate
-- Run this, wait 60 seconds, run again, compute delta
SHOW MASTER STATUS;
-- Note Position value
-- After 60 seconds:
SHOW MASTER STATUS;
-- Delta position / 60 = bytes per second of binlog generation
-- Alternatively, query binlog size over time via CloudWatch
-- Metric: BinLogDiskUsage (under RDS namespace)
aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name BinLogDiskUsage \
--dimensions Name=DBClusterIdentifier,Value=your-cluster-id \
--start-time $(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
--end-time $(date -u '+%Y-%m-%dT%H:%M:%SZ') \
--period 3600 \
--statistics Maximum Average \
--region us-east-1
Multiply your hourly binlog generation rate by your retention window in hours to get the steady-state binlog storage footprint. At 10 GB/hour and 24 hours retention: 240 GB at $0.10/GB-month = $24/month just for binlog retention. At 50 GB/hour (common for high-write OLTP): 1.2 TB = $120/month in binlog storage alone.
The surprise is not the absolute number — it is that teams enabling binlog for a single DMS task or Debezium connector discover this cost only after the first bill, because there is no upfront cost display in the RDS console when you enable binlog.
The Three Scenarios Where This Becomes Expensive
Scenario 1: DMS Task Left Running After Migration
AWS DMS CDC tasks consume binlog as they run. While the task is active, binlog is consumed and retention is managed around the task's current position. When the task is stopped — after migration cutover, when a pipeline is decommissioned, or simply when someone pauses it to "debug later" — the binlog consumption stops. Aurora continues generating and retaining binlogs for the full retention window regardless.
A stopped DMS task with binlog retention hours set to 24 means 24 hours of accumulated binlog that nobody is consuming. If the task stays stopped for 3 days, you pay for 24 hours worth of binlog storage continuously (old files expire as new ones accumulate, so the steady-state storage is rate × retention_hours, not growing unbounded — but it is still a fixed ongoing cost for a paused pipeline).
When a DMS task is permanently decommissioned, reduce binlog retention to the minimum needed for any remaining consumers, or disable binlog entirely if no CDC consumers remain.
Scenario 2: Binlog Enabled "Just in Case" with No Active Consumer
Teams sometimes enable binlog during a migration project for a CDC consumer that never ended up being used, or enable it preemptively for a future Debezium integration that has not been built yet. With no active consumer advancing through the binlog, the retention window accumulates the full window of data continuously.
-- Check if any replication consumers are actively reading binlogs
SHOW SLAVE HOSTS;
-- Lists connected MySQL replicas
-- For DMS: check via AWS console or CLI
aws dms describe-replication-tasks \
--filter Name=replication-task-arn,Values=your-task-arn \
--query 'ReplicationTasks[].{Status:Status,StopReason:StopReason}'
-- If no active consumers exist, disable binlog
-- Remove binlog_format from the custom parameter group
-- OR reduce retention to minimum (1 hour) as an interim step
CALL mysql.rds_set_configuration('binlog retention hours', 1);
Scenario 3: Retention Set Too High for the Consumer's Actual Lag
The typical guidance is to set binlog retention long enough that a consumer can fall behind and catch up without losing its position. For DMS, a 24-hour retention window means a CDC task can be paused for up to 24 hours and resume without needing to restart from scratch. For most pipelines, this is far more headroom than necessary.
If your DMS task or Debezium connector runs continuously with less than 1 hour of typical lag, a 2-hour retention window provides adequate safety margin while reducing retained binlog storage by 12x compared to 24 hours. The right retention window is: your acceptable recovery window if the consumer falls behind — not a generic "safe" number.
-- Set binlog retention to 2 hours (adjust based on your consumer's lag tolerance)
CALL mysql.rds_set_configuration('binlog retention hours', 2);
-- Verify the change took effect
CALL mysql.rds_show_configuration;
-- Monitor consumer lag to confirm 2 hours is sufficient headroom
-- For DMS: CDCLatencySource metric in CloudWatch
aws cloudwatch get-metric-statistics \
--namespace AWS/DMS \
--metric-name CDCLatencySource \
--dimensions Name=ReplicationInstanceIdentifier,Value=your-replication-instance \
--start-time $(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
--end-time $(date -u '+%Y-%m-%dT%H:%M:%SZ') \
--period 3600 \
--statistics Maximum \
--region us-east-1
Binlog Format: ROW vs MIXED vs STATEMENT
Beyond retention, binlog format affects both storage consumption and replication fidelity. Aurora MySQL supports three formats:
- STATEMENT: Logs the SQL statement. Low storage overhead, but non-deterministic functions (NOW(), UUID(), RAND()) produce different results on replicas. Not safe for CDC-based migration.
- ROW: Logs the before and after image of each changed row. Deterministic, safe for CDC. Storage overhead is proportional to the number of rows changed — a single UPDATE that touches 1M rows generates 1M row images in the binlog.
- MIXED: Uses STATEMENT for safe statements, ROW for non-deterministic ones. Reduces storage compared to ROW, but some CDC tools require pure ROW format.
AWS DMS and Debezium both require ROW format. If you are running ROW format with large batch DML operations (bulk updates, batch deletes), the binlog size per operation is magnified significantly compared to STATEMENT format. A single DELETE FROM events WHERE created_at < '2024-01-01' that removes 50 million rows generates 50 million row deletion events in ROW-format binlog.
-- Check current binlog format
SHOW VARIABLES LIKE 'binlog_format';
-- For large batch DML with ROW format, consider chunking deletes
-- to limit per-operation binlog volume
DO
BEGIN
DECLARE rows_deleted INT DEFAULT 1;
WHILE rows_deleted > 0 DO
DELETE FROM events
WHERE created_at < '2024-01-01'
LIMIT 10000;
SET rows_deleted = ROW_COUNT();
DO SLEEP(0.1); -- brief pause to reduce replication lag
END WHILE;
END;
Monitoring Binlog Storage in CloudWatch
Set up a CloudWatch alarm on BinLogDiskUsage to alert when binlog storage exceeds a threshold you define based on your acceptable cost ceiling:
aws cloudwatch put-metric-alarm \
--alarm-name aurora-binlog-storage-high \
--alarm-description "Aurora MySQL binlog storage exceeds 50GB" \
--metric-name BinLogDiskUsage \
--namespace AWS/RDS \
--statistic Maximum \
--period 3600 \
--evaluation-periods 1 \
--threshold 53687091200 \
--comparison-operator GreaterThanThreshold \
--dimensions Name=DBClusterIdentifier,Value=your-cluster-id \
--alarm-actions arn:aws:sns:us-east-1:123456789:dba-alerts \
--region us-east-1
The Decision Tree
Before enabling binlog on an Aurora MySQL cluster, answer these questions:
- Do you have an active, running CDC consumer that requires binlog? If no, do not enable it.
- What is your consumer's maximum acceptable lag before it needs to restart from scratch? Set retention to that window plus 20% headroom — not 24 hours by default.
- Does your workload include large batch DML? If yes, factor the ROW-format amplification into your storage cost estimate.
- When this CDC consumer is decommissioned, who is responsible for reducing the retention window or disabling binlog?
Binlog on Aurora MySQL is not a free feature. It has a storage cost that scales directly with your write volume and your retention window. Treat it as a configuration that requires a cost justification, not a default setting you enable once and forget.
Unexpected Aurora storage costs on your bill?
We audit Aurora MySQL binlog configuration, DMS task hygiene, and storage cost drivers as part of a free cloud cost assessment. Findings in 5–7 business days.