Summary of extensive disk I/O investigation — findings and conclusions
After spending considerable time investigating the high disk I/O on my servers (with help from an Claude PRO AI assistant, especially for this issue I subscribed to PRO!), I want to share my findings for anyone else experiencing this issue.
Setup: 3 servers running Cloudron v9.1.3, Ubuntu 22.04. Server 1 (just to focus on one): 12 WordPress sites, Matomo, EspoCRM, FreeScout (2x), Roundcube, MiroTalk, Taiga, MainWP, Yourls, Surfer (2x). Constant write I/O of ~2.5 MB/s = ~347 GB/day.
Reference: Cloudron demo server (20 apps including Nextcloud, Matrix, Discourse) shows ~80 GB/day. My servers run 4-5x higher with lighter apps.
What we investigated and measured
iotop analysis: Docker MySQL (messageb) and host MySQL are by far the largest writers
MySQL general log analysis: mapped write distribution per table
Tested innodb_flush_log_at_trx_commit = 2: changes the pattern (bursts instead of constant pressure) but total write volume unchanged
Analyzed nginx access logs for suspicious traffic patterns
Compared against Cloudron demo server
What was cleaned up (almost no impact)
EspoCRM: deleted 244K jobs + 244K scheduled_job_log_records; set cleanupJobPeriod to 7 days
WordPress actionscheduler_claims: deleted 130K rows
Roundcube: reduced from 5 to 1 installation
Matomo: adjusted session_gc_probability and login_cookie_expire; cleared accumulated sessions
Wordfence: reduced live traffic table to 200 rows / 1 day, disabled audit logging
MainWP: disabled uptime monitor addon and SSL monitor addon
MainWP wp_mainwp_wp_logs: deleted 46,903 rows older than 30 days
MainWP wp_mainwp_wp_logs_meta: deleted 141,682 orphaned records
MainWP: disabled Network Activity logging
What was ruled out as significant I/O cause
Matomo: stopped the app entirely → no measurable difference in I/O
MainWP: one of the three servers has no MainWP but shows identical I/O pattern
FreeScout: job tables are empty
External scan traffic: all returning 404/301 from nginx, no database impact
What is proven but not fixable without Cloudron
Matomo healthcheck bug: GET / triggers the LoginOIDC plugin on every health check (every 10 seconds), creating a new MySQL session each time → 8,640 new sessions per day per Matomo instance. Fix requires changing the health check endpoint from GET / to /matomo.js in the app package. This is a Cloudron-side fix. Reported separately in topic 15211.
InnoDB configuration: innodb_log_file_size is only 48MB (causes very frequent checkpoints), innodb_flush_method is fsync. These settings are suboptimal for a write-heavy workload but are managed by Cloudron.
go-carbon/Graphite: writes ~0.13 MB/s continuously for 814 whisper metric files — inherent to Cloudron's monitoring stack.
Conclusion
There is no single large cause. The high I/O is the sum of multiple Cloudron-internal mechanisms. Everything works correctly — no performance issues, no user impact. But for a server with relatively low user traffic, 347 GB/day of writes feels disproportionate, especially compared to the Cloudron demo server at ~80 GB/day.
Sharing this in case it helps others investigating the same issue.