When MySQL won't start
You restart MySQL and the service refuses to come up. The error log shows:
InnoDB: Database page corruption on disk or a failed file read of page X
InnoDB: Cannot continue operationWordPress is dead. Every request times out or shows "Error establishing database connection." This is the worst moment in WordPress recovery.
The good news: in most cases, the data is recoverable. The procedure is innodb_force_recovery, escalated level by level.
Stage 1 — Confirm corruption is the cause
systemctl status mysql
tail -100 /var/log/mysql/error.logLook for InnoDB messages. Common patterns:
Database page corruption on disk— pages are damagedPage lsn X but it should be Y— log sequence numbers don't matchCannot find the appropriate table to look up— data dictionary inconsistency
These all indicate InnoDB corruption. Proceed with innodb_force_recovery.
The recovery levels
InnoDB has 6 escalating recovery levels (1-6). Each subsequent level is more invasive but enables MySQL to start in more broken states.
Edit /etc/mysql/my.cnf (or /etc/my.cnf depending on OS):
[mysqld]
innodb_force_recovery = 1Restart MySQL:
systemctl start mysqlIf MySQL starts, you can connect. If not, increase to 2, then 3, etc., up to 6.
What each level does
Level 1 (SRV_FORCE_IGNORE_CORRUPT): lets server run even if it detects corrupt page. Read-only operations possible.
Level 2 (SRV_FORCE_NO_BACKGROUND): prevents the master thread from running. Skip purging operations.
Level 3 (SRV_FORCE_NO_TRX_UNDO): doesn't run transaction rollback. Inconsistent state possible.
Level 4 (SRV_FORCE_NO_IBUF_MERGE): prevents insert buffer merge operations.
Level 5 (SRV_FORCE_NO_UNDO_LOG_SCAN): doesn't look at undo logs at all. Treats incomplete transactions as committed.
Level 6 (SRV_FORCE_NO_LOG_REDO): doesn't apply redo logs. Most destructive — data after last checkpoint is lost.
For WordPress recovery, you almost always want the LOWEST level that allows MySQL to start. Higher levels = more data loss.
The recovery procedure
Step 1 — Try level 1
innodb_force_recovery = 1Restart. If MySQL starts, jump to Step 4.
Step 2 — Escalate if needed
If level 1 fails, try 2, then 3, then 4. After each:
systemctl start mysqlStop at the first level that allows start.
Step 3 — At level 5 or 6, expect data loss
Levels 5 and 6 mean some recent data is lost. Accept this and proceed.
Step 4 — Verify what data is intact
mysql -u root -pUSE your_wp_database;
SELECT COUNT(*) FROM wp_posts;
SELECT COUNT(*) FROM wp_users;
SELECT MAX(post_date) FROM wp_posts WHERE post_status = 'publish';If counts look reasonable and the latest post date is recent, you have most of your data.
Step 5 — Export ALL data immediately
mysqldump --single-transaction --quick --lock-tables=false \
--no-tablespaces \
your_wp_database > /backup/recovery-dump.sqlThis is critical: you must export everything before doing anything else. The current MySQL state is fragile.
Step 6 — Stop MySQL, delete corrupted data files
systemctl stop mysql
cp -a /var/lib/mysql /var/lib/mysql.broken.$(date +%Y%m%d)
rm -rf /var/lib/mysql/your_wp_databaseStep 7 — Remove innodb_force_recovery and restart
# innodb_force_recovery = 1 ← comment this outsystemctl start mysqlMySQL should start cleanly now (no more corruption since data dir is gone).
Step 8 — Recreate database, import dump
CREATE DATABASE your_wp_database;mysql your_wp_database < /backup/recovery-dump.sqlStep 9 — Test WordPress
cd /var/www/yoursite
wp option get siteurl
wp post list --posts_per_page=5If queries return data, WordPress is back.
Why this works
The corruption is in InnoDB's internal data structures, not your raw row data. innodb_force_recovery lets you bypass enough internal logic to read the rows. Dumping them gives you a clean SQL file. Importing into a fresh database recreates everything cleanly.
The data is the same; only the storage format is rebuilt.
What data might be lost
At levels 1-3: typically nothing. At level 4: insert buffer merges may not have happened — some recent inserts could be missing. At level 5: incomplete transactions are not rolled back — committed-but-not-flushed transactions may be lost. At level 6: changes since last checkpoint are lost — typically the last 30-60 minutes.
For WordPress, the data most likely to be lost is: - Last few orders (WooCommerce) - Comments posted in the last hour - Most recent log entries
Preventing future corruption
After recovery, configure MySQL for resilience:
[mysqld]
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
innodb_doublewrite = ON
innodb_file_per_table = 1
innodb_flush_method = O_DIRECTSet up monitoring:
# Daily integrity check
0 3 * * * mysqlcheck --check --all-databases --silent > /var/log/mysqlcheck.log 2>&1And REAL backups:
- Binary backups:
mariabackuporxtrabackupfor fast point-in-time recovery - Off-site copies: rsync to a separate server or S3 nightly
- Restore drills: monthly test that you can actually restore
Common mistakes during MySQL corruption recovery
- Restarting MySQL repeatedly without changing config — doesn't help; corruption persists
- Going straight to level 6 — loses more data than necessary; always start at 1
- Not exporting before destroying data — once you delete /var/lib/mysql/<db>, that data is gone if dump failed
- Removing innodb_force_recovery before exporting — MySQL goes back to refusing to start
When to call a specialist
InnoDB recovery is high-stakes. A wrong step at the wrong level can lose data permanently. We've recovered hundreds of corrupted WordPress databases without data loss.
Database recovery emergency within hours. For broader recovery see emergency support.

