MySQL

MySQL binlog Retention, Rotation & Purge: Production Guide (2026)

Configure MySQL binlog retention safely: binlog_expire_logs_seconds, manual purging rules, AWS RDS retention, and the disk-exhaustion failure mode you should monitor for.

JusDB Team
May 9, 2026
10 min read
244 views

TL;DR — MySQL binlog retention without disk surprises: Use binlog_expire_logs_seconds (MySQL 8.0+, replaces deprecated expire_logs_days) to set retention — typical safe value is 604800 seconds (7 days). Don't PURGE BINARY LOGS BEFORE manually unless you've confirmed every replica's Read_Source_Log_Pos is past the boundary. On AWS RDS use mysql.rds_set_configuration('binlog retention hours', 168) instead of the in-instance variable. Monitor binlog disk usage (SHOW BINARY LOGS) — flow-control on a stalled replica can let binlogs grow to TB before alerting. Rotate manually with FLUSH BINARY LOGS after maintenance, never to 'save space'.

Binary logs are the heartbeat of MySQL replication and point-in-time recovery — but left unmanaged, they silently consume disk space until your database server grinds to a halt at 3 AM. Every production MySQL deployment eventually faces the same reckoning: gigabytes of binlog files piling up on a volume that was never sized to hold them indefinitely. Understanding how binary logs rotate, expire, and can be safely purged is not optional knowledge for a DBA — it is survival knowledge. This guide covers the full lifecycle of MySQL binary logs, from configuration through safe deletion, with the guardrails you need to avoid destroying a replication chain or losing your recovery window.

TL;DR
  • Use binlog_expire_logs_seconds (MySQL 8.0+) instead of the deprecated expire_logs_days for automatic expiry.
  • Always check SHOW REPLICA STATUS before manually purging to confirm replicas have consumed the logs you plan to drop.
  • Use PURGE BINARY LOGS TO or PURGE BINARY LOGS BEFORE for targeted manual cleanup.
  • Set sync_binlog=1 in production for durability — never trade crash safety for performance without understanding the risk.
  • On RDS, use mysql.rds_set_configuration('binlog retention hours', N) instead of native MySQL variables.
  • Monitor disk usage continuously; binlog volume spikes during heavy write workloads without warning.

What Are Binary Logs?

MySQL binary logs (binlogs) are a sequential record of every change that modifies data in the database. Unlike the general query log, binlogs capture only data-modifying statements — INSERT, UPDATE, DELETE, CREATE TABLE, DROP, and so on. They do not record SELECT queries.

Binary logs serve two critical purposes in production systems:

  • Replication: Replicas read the primary's binlog stream to stay synchronized. Without binlogs, MySQL replication does not function.
  • Point-in-time recovery (PITR): After restoring a full backup, you replay binlogs forward to recover to a specific moment before a failure or accidental deletion.

Binary logging is enabled by default in MySQL 8.0. On older installations you may need to verify it explicitly:

sql
SHOW VARIABLES LIKE 'log_bin';

If the result is ON, binary logging is active. If it is OFF, add log_bin to your my.cnf and restart — but be aware this requires a server restart and has implications for all downstream consumers.

How Binary Log Rotation Works

MySQL writes to a single active binlog file until one of several rotation triggers fires, at which point it closes that file and opens a new one with an incremented sequence number (e.g., mysql-bin.000001mysql-bin.000002).

Rotation occurs when:

  • The current file reaches max_binlog_size (default: 1 GB).
  • The server is restarted or FLUSH LOGS is executed.
  • A RESET BINARY LOGS AND GTIDS (formerly RESET MASTER) statement is issued.

Each rotation produces a new file. The index file (mysql-bin.index) maintains the ordered list of all current binlog files on disk. This is the file MySQL consults to know which logs exist and which can be cleaned up.

Warning

Never manually delete binlog files from the filesystem using rm. Doing so orphans entries in the index file, which corrupts MySQL's view of its own log state. Always use SQL commands or let the automatic expiry system handle deletions.

Configuring Expiry and Retention

Automatic cleanup is the first line of defense against disk exhaustion. MySQL provides two variables for this, and which one you use depends on your server version.

MySQL 8.0+: binlog_expire_logs_seconds

binlog_expire_logs_seconds sets the retention window in seconds. A binary log file is eligible for automatic deletion once it is older than this threshold and a rotation event occurs.

ini
# my.cnf
[mysqld]
log_bin                    = /var/lib/mysql/mysql-bin
max_binlog_size            = 512M
binlog_expire_logs_seconds = 604800   # 7 days in seconds

You can also change it at runtime without a restart:

sql
SET GLOBAL binlog_expire_logs_seconds = 604800;

MySQL 5.7 and Earlier: expire_logs_days

On older versions, the equivalent variable is expire_logs_days:

ini
# my.cnf (MySQL 5.7)
expire_logs_days = 7
Warning

In MySQL 8.0, expire_logs_days is deprecated. If both variables are set simultaneously, binlog_expire_logs_seconds takes precedence. Do not rely on expire_logs_days in MySQL 8.0 deployments — it may be removed in a future release.

Choosing a Retention Window

Your retention window should be long enough to cover your backup cycle plus a comfortable recovery buffer. A common baseline is 7 days, but production teams with longer RTO requirements often extend to 14 or 30 days. The tradeoff is direct: longer retention means more disk space consumed. Size your binlog volume accordingly before extending retention.

binlog_format: ROW vs STATEMENT

The format in which events are written to the binlog significantly affects both file size and behavior:

ini
binlog_format = ROW
  • ROW (recommended): Records the actual row changes. Larger files, but deterministic — the replica knows exactly which rows changed regardless of non-deterministic functions.
  • STATEMENT: Records the SQL statement itself. More compact, but unsafe with NOW(), RAND(), and other non-deterministic functions — can cause replica drift.
  • MIXED: Uses STATEMENT by default and falls back to ROW for unsafe statements. A historical compromise that most modern deployments avoid.

For production replication and PITR, ROW format is the right choice. Expect binlog files to be larger than with STATEMENT format — factor this into your disk planning.

sync_binlog for Durability

The sync_binlog variable controls how often MySQL flushes the binlog to disk:

ini
sync_binlog = 1   # Flush to disk after every commit (safest)

With sync_binlog=1, MySQL calls fsync() after every transaction commit, ensuring no committed transactions are lost in a crash. The performance cost is real on high-throughput systems, but the data safety guarantee is essential for production primaries. Setting it to 0 delegates flushing to the OS — faster, but you risk losing the last few seconds of committed transactions on a crash.

Purging Binary Logs Safely

Automatic expiry handles routine cleanup, but there are scenarios where you need to manually reclaim disk space immediately. MySQL provides two targeted purge commands.

Step 1: Check What You Have

sql
SHOW BINARY LOGS;

This returns a list of all binlog files with their sizes. Identify the oldest files you want to remove.

Step 2: Verify Replica Status

This is the step most people skip — and it is the one that breaks replication. Before purging any log, confirm your replicas have already consumed it:

sql
-- Run on each replica
SHOW REPLICA STATUS\G

Look at the Source_Log_File field (formerly Master_Log_File). This tells you which binlog file the replica is currently reading from. Never purge any file with a sequence number equal to or greater than this value.

Warning

If you purge a binlog file that a replica still needs, the replica's IO thread will fail with "Got fatal error 1236 from source when reading data from binary log." Recovering from this requires either re-syncing the replica from a backup or relying on GTID-based auto-positioning, which is not always available in every configuration.

Step 3: Purge

To purge all logs up to (but not including) a specific file:

sql
PURGE BINARY LOGS TO 'mysql-bin.000042';

To purge all logs older than a specific timestamp:

sql
PURGE BINARY LOGS BEFORE '2026-02-15 00:00:00';
Tip

When using PURGE BINARY LOGS BEFORE, set the timestamp at least 24 hours behind the current time to account for any replication lag on your replicas. A lagging replica may still need logs that appear old by wall clock time.

RDS and Cloud-Managed MySQL

On Amazon RDS for MySQL, you cannot set binlog_expire_logs_seconds directly via parameter groups. RDS manages binlog lifecycle through its own mechanism. Use the provided stored procedure to configure retention:

sql
-- Set binlog retention to 72 hours on RDS
CALL mysql.rds_set_configuration('binlog retention hours', 72);

-- Verify
CALL mysql.rds_show_configuration();

RDS defaults to NULL (no retention limit) if this is never configured, which means binlogs accumulate indefinitely. Set this immediately after provisioning any RDS MySQL instance that uses replication or PITR.

Monitoring Disk Usage

Disk exhaustion from binary logs is a foreseeable and preventable failure. Build monitoring around it rather than reacting to it after the fact.

Checking Current Binlog Disk Usage

sql
-- Total size of all binary logs on disk
SELECT
    COUNT(*) AS log_count,
    ROUND(SUM(File_size) / 1024 / 1024 / 1024, 2) AS total_size_gb
FROM information_schema.FILES
WHERE FILE_TYPE = 'UNDO LOG';

-- Alternative: sum from SHOW BINARY LOGS
SELECT
    COUNT(*) AS log_count,
    ROUND(SUM(File_size) / 1073741824, 2) AS total_gb
FROM (
    SELECT File_size FROM mysql.`general_log` LIMIT 0  -- placeholder
) t;

The most reliable approach is querying the output of SHOW BINARY LOGS directly or checking filesystem usage on the binlog directory:

bash
du -sh /var/lib/mysql/mysql-bin.*
df -h /var/lib/mysql

Automated Cleanup Script

For self-managed MySQL servers without automated expiry events, a simple cron-based approach works well. This script checks replica consumption before purging:

bash
#!/bin/bash
# safe-binlog-purge.sh
# Purge binlogs older than 7 days, only if replicas are current

MYSQL="mysql -u root -p${MYSQL_PWD}"
RETENTION_DAYS=7
CUTOFF=$(date -d "-${RETENTION_DAYS} days" '+%Y-%m-%d %H:%M:%S')

# Get the oldest log file still needed by any replica
REPLICA_LOG=$($MYSQL -e "SHOW REPLICA STATUS\G" 2>/dev/null \
  | grep "Source_Log_File" | awk '{print $2}')

if [ -z "$REPLICA_LOG" ]; then
  echo "No replica detected or SHOW REPLICA STATUS returned empty."
  echo "Proceeding with time-based purge with caution."
fi

echo "Purging binary logs before: $CUTOFF"
$MYSQL -e "PURGE BINARY LOGS BEFORE '$CUTOFF';"
Tip

Alert when the binlog directory exceeds 70% of the volume's total capacity. By the time you hit 90%, you may not have enough headroom to take an emergency backup before the disk fills completely.

Metrics to Track

  • Binlog disk usage (GB) — alert at 70% volume capacity.
  • Binlog write rate (MB/s) — spikes indicate heavy write workloads that will consume retention faster.
  • Seconds behind source — replica lag means replicas need older logs; increasing lag widens the window of logs you cannot safely delete.
  • Number of binlog files — a proxy for whether automatic expiry is functioning correctly.

Key Takeaways

Key Takeaways
  • Binary logs are essential for replication and PITR — they must be managed, not disabled, to reclaim disk space.
  • Use binlog_expire_logs_seconds on MySQL 8.0+; expire_logs_days is deprecated and will be removed.
  • Always run SHOW REPLICA STATUS on every replica before manual purging — never guess which logs are safe to drop.
  • Use PURGE BINARY LOGS TO for file-based targeting and PURGE BINARY LOGS BEFORE for time-based targeting; never use rm on binlog files directly.
  • Set sync_binlog=1 in production to prevent committed transaction loss on crash.
  • Use ROW binlog format for deterministic replication; account for its larger file sizes in disk planning.
  • On RDS, configure retention via mysql.rds_set_configuration('binlog retention hours', N) — it defaults to unlimited and will fill your storage.
  • Build proactive disk monitoring around your binlog directory; do not wait for alerts after the volume is full.

Simplify MySQL Operations with JusDB

Binary log management is one of dozens of operational tasks that compound into significant overhead for database teams running MySQL at scale. Tracking retention windows, validating replica state before every purge, and monitoring disk across multiple servers adds up — especially when your team would rather be building product than firefighting storage alerts.

JusDB provides fully managed MySQL hosting with automated binary log lifecycle management built in. Retention policies are applied consistently, disk usage is monitored continuously, and replica health is validated before any cleanup operation runs. You get the reliability of a production-hardened MySQL environment without the operational burden of managing it yourself.

Explore JusDB managed MySQL and see how much overhead you can hand off.

Frequently Asked Questions

What replaces expire_logs_days in MySQL 8.0?
binlog_expire_logs_seconds (default 2592000 — 30 days). The old expire_logs_days is deprecated since 8.0 and will be removed in a future version. Set the new variable in seconds: SET PERSIST binlog_expire_logs_seconds = 604800; for 7-day retention.
How do I set binlog retention on AWS RDS?
RDS doesn't honor the binlog_expire_logs_seconds parameter the same way — use the AWS stored procedure: CALL mysql.rds_set_configuration('binlog retention hours', 168); for 7-day retention. Verify with CALL mysql.rds_show_configuration;.
Is it safe to manually PURGE BINARY LOGS?
Only if every replica has fully consumed the binlogs you're about to purge. Check on each replica: SHOW REPLICA STATUS\G and read Source_Log_File + Read_Source_Log_Pos. Purge to before that file. If you purge ahead of a replica, the replica breaks and you must rebuild it from a backup.
What does FLUSH BINARY LOGS actually do?
It closes the current binlog file and opens a fresh one with the next sequence number. It does NOT delete anything. Use it after maintenance or backup operations to create a clean rotation point. Repeated FLUSHes will create empty binlogs but free no disk.
How much disk space should I budget for MySQL binary logs?
Budget 2× the retention window's typical traffic, plus headroom for replica lag spikes. Rule: if your busy-hour binlog generation is 20 GB/hr × 168 hr retention = 3.4 TB. If a replica stalls and you can't expire those logs, that's your disk floor. Alert at 70% of binlog volume usage.
Why is my binlog disk filling up despite retention being set?
MySQL won't delete binlogs that any active replica is still reading. If a replica is stalled (network issue, lock wait, or stopped), the source can't expire the binlog, and disk fills. Diagnose: SHOW PROCESSLIST to see if there's an active dump thread, SHOW REPLICA STATUS on each replica to find the stuck one.
Should binlog retention be longer than my backup window?
Yes. Binlog retention should be at least 2× your backup interval + safety margin. Daily backup → 48–72 hr retention minimum. Otherwise point-in-time recovery (replaying binlogs against a backup) is impossible.
What's the difference between binlog_expire_logs_seconds and expire_logs_days?
Same purpose, different unit. expire_logs_days (deprecated) takes integer days; binlog_expire_logs_seconds (MySQL 8.0+) takes seconds and supports finer granularity. If both are set, binlog_expire_logs_seconds wins. Use only the seconds variant on 8.0+.

Share this article

Deeper Reading

Curated companion guides for readers who want to go deeper on this topic.