How a B2B SaaS Team Migrated 8 TB from Self-Managed MySQL to Aurora with Zero Downtime
A 120-engineer SaaS company moved 8 TB of MySQL data to Aurora MySQL with no service interruption, using DMS continuous replication, schema versioning, and a traffic-shifting cutover that took under 4 minutes.
Data & Storage · advanced · 12 min read · 2688 words · By Sandip Das · Published
What this covers
- The Situation
- The Problem
- Constraints
- Options Considered
- The Solution
- Implementation
- Results
- What Went Wrong Along the Way
- Lessons Learned
- Apply It Yourself
Key numbers
| Metric | Before | After |
|---|---|---|
| P99 API latency during peak payroll window | 4,200 ms | 310 ms |
| Database I/O utilisation at peak | 97% | 41% |
| Unplanned database downtime (trailing 6 months) | 94 minutes | 0 minutes |
| On-call DB alerts per week | 16.1 | 1.4 |
| Database tier infrastructure cost per month | $28,000 | $19,400 |
Key components
- AWS Database Migration Service (DMS) CDC — Continuously replicated changes from the MySQL binary log to Aurora, keeping the target within seconds of the source and enabling a traffic-flip cutover rather than a data-move cutover.
- RDS Proxy — Pooled up to 3,200 application connections into a manageable Aurora connection count and served as the single endpoint that the cutover flag redirected, making the switch transparent to the application.
- AWS Parameter Store feature flag — Stored the cutover decision as a runtime configuration value so the endpoint switch could be executed and rolled back in seconds without a code deployment.
- Flyway schema manager — Applied DDL migrations to Aurora independently of the DMS replication task, keeping schemas synchronised throughout the CDC phase and eliminating schema drift as a cutover risk.
- CloudWatch CDC lag alarm with Lambda auto-restart — Detected DMS task failures caused by transient network issues and automatically restarted the CDC task, removing a manual intervention requirement from the cutover runbook.
- Aurora MySQL 3.x multi-AZ cluster — Provided the target database with sub-second read replica lag, automatic storage scaling, continuous S3 backups, and zero-downtime failover — capabilities unavailable on the self-managed colo hardware.
The Situation
The company is a 120-engineer B2B SaaS platform serving mid-market HR and payroll customers across North America and Europe. Their core product — payroll processing, benefits administration, and compliance reporting — runs on a monolithic Rails application backed by a self-managed MySQL 5.7 cluster hosted on three bare-metal servers in a co-location facility in Northern Virginia. The database tier holds approximately 8 TB of data spread across 340 tables, with the largest tables being payroll_runs (1.1 TB), audit_events (900 GB), and employee_documents (1.4 TB, mostly BLOBs).
The engineering team is split into six product squads and a platform team of twelve. The platform team owns infrastructure, reliability, and the migration project. They run Terraform for infrastructure-as-code, deploy via GitHub Actions to AWS, and use Datadog for APM and alerting. The application tier already lives in AWS (us-east-1), so the database sitting in a colo was already a cross-environment latency problem.
The rest of this case study — the trade-offs, the scaling numbers and the build prompt — is available to LearnXops members. Create an account or see membership.
Topics: mysql, aurora, database-migration, aws-dms, zero-downtime, data
Related case studies
- How a B2B SaaS Company Turned a Swampy S3 Data Lake into a Lakehouse with Apache Iceberg
- How a B2B SaaS Team Cut Postgres P99 Latency by 73% with Pooling, Replicas and Partitioning
- How Discord Migrated Trillions of Messages from Cassandra to ScyllaDB
- How Figma Scaled Postgres from One Overloaded Database to Vertical Partitioning and Horizontal Sharding
- The 2017 GitLab Database Deletion: When Five Backup Methods All Failed at Once
- The GitHub October 2018 Outage: How 43 Seconds of Network Partition Caused 24 Hours of Data Inconsistency