New to KubeDB? Please start here.
MySQL Database Migration
This guide will show you how to use KubeDB Migrator to migrate an existing MySQL database — such as one running on AWS RDS or any external instance — entirely into a KubeDB-managed MySQL with minimal downtime.
Before You Begin
At first, you need to have a Kubernetes cluster, and the
kubectlcommand-line tool must be configured to communicate with your cluster.Install
KubeDBoperator with the Migrator operator enabled in your cluster following the steps here.The source
MySQLinstance must be network-reachable from within your Kubernetes cluster.The source
MySQLinstance must have binary logging enabled withbinlog_format=ROWandbinlog_row_image=FULL. The database user provided for migration must have replication privileges.You should be familiar with the following
KubeDBconcepts:
To keep everything isolated, we are going to use a separate namespace called demo throughout this tutorial.
$ kubectl create ns demo
namespace/demo created
Prepare Source Connection Information
First, create an authentication secret to communicate with the source MySQL database:
$ kubectl create secret generic source-mysql-auth -n demo \
--type=kubernetes.io/basic-auth \
--from-literal=username=<username> \
--from-literal=password=<password>
Now create an AppBinding with the necessary information. The Migrator operator reads the source MySQL connection information from this AppBinding CR. Use the following YAML to create your AppBinding:
apiVersion: appcatalog.appscode.com/v1alpha1
kind: AppBinding
metadata:
name: source-mysql
namespace: demo
spec:
type: mysql
version: "8.4.8"
clientConfig:
url: "mysql://host:port"
secret:
name: source-mysql-auth
Here,
spec.clientConfig.urlis the connection URL of the source MySQL instance.spec.secret.nameis the reference to the secret we created earlier, containing the MySQL authentication information.
For a
KubeDB-managed database, anAppBindingis created by default. So there is no need to create one for the target database.
Create Target MySQL Database
KubeDB implements a MySQL CRD to define the specification of a MySQL database. Follow the MySQL object to create the target database.
apiVersion: kubedb.com/v1
kind: MySQL
metadata:
name: target-mysql
namespace: demo
spec:
version: "8.4.8"
storageType: Durable
storage:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 30Gi
deletionPolicy: Delete
$ kubectl create -f https://github.com/kubedb/docs/raw/v2026.4.27/docs/guides/mysql/quickstart/yamls/quickstart-v1.yaml
mysql.kubedb.com/mysql-quickstart created
Note: Adjust the
resources.requests.storagebased on source database.
Wait untill target-mysql has status Ready
Apply Migrator CR
To Migrate database we have to create a Migrator CR. Below is the YAML of the Migrator CR that we are going to create,
apiVersion: migrator.kubedb.com/v1alpha1
kind: Migrator
metadata:
name: mysql-migrate
namespace: demo
spec:
jobTemplate:
spec:
securityContext:
fsGroup: 65534
source:
mysql:
connectionInfo:
appBinding:
name: source-mysql
namespace: demo
dbName: "mysql"
maxConnections: 100
schema:
enabled: true
database: [] # database to include
excludeDatabase: [] # database to exclude
snapshot:
enabled: true
pipeline:
workers: 3
sinkers: 4
buffer: 12
write_batch_size: 200
read_batch_size: 1000
streaming:
enabled: true
target:
mysql:
connectionInfo:
appBinding:
name: target-mysql
namespace: demo
dbName: "mysql"
maxConnections: 100
Here,
spec.source / spec.target — connectionInfo:
appBinding.name/appBinding.namespace— references theAppBindingfor the source or target MySQL instance.dbName— the internal database used as the initial connection entry point.maxConnections— limits the number of concurrent connections the migrator opens to this MySQL instance.
spec.source.schema — schema migration phase:
enabled: true— enables the schema migration phase.database— list of databases to include; empty means all databases.excludeDatabase— list of databases to exclude from migration.
spec.source.snapshot — bulk snapshot phase:
enabled: true— enables the initial bulk snapshot phase.pipeline.workers— number of parallel workers, each processing a separate table concurrently.pipeline.sinkers— number of parallel write workers pushing data to the target for each worker.pipeline.buffer— size of the in-memory queue (in records) between readers and writers.pipeline.read_batch_size— number of rows fetched per read batch from the source.pipeline.write_batch_size— number of rows written per batch to the target.
spec.source.streaming — CDC streaming phase:
enabled: true— enables change-data capture streaming after the snapshot completes, keeping the target continuously in sync with ongoing changes on the source.
Watch Migration Progress
Let’s wait for the LAG to reach near zero . Run the following command to watch Migrator CR,
Every 2.0s: kubectl get migrator -n demo
NAME PHASE DBTYPE STAGE LAG PROGRESS AGE
mysql-migrate Running mysql Streaming 0B 4h36m
Cutover
Once the LAG drops to near zero, stop all writes to the source database. Wait until the LAG reaches exactly zero — at that point both databases are fully in sync.
Now delete the Migrator CR to stop the migration process:
$ kubectl delete migrator -n demo mysql-migrate
migrator.migrator.kubedb.com "mysql-migrate" deleted
Finally, update your application’s connection string to point to the target KubeDB-managed MySQL database. The migration is complete.































