Overview
This article walks the installer through migrating the LILT application database from an in-cluster MySQL deployment to an Amazon RDS for MySQL instance. It applies when moving an existing self-managed installation onto a fully managed cluster, such as the environment described in Install System (AWS EKS), where RDS replaces the in-cluster database. The database is exported through a proxied connection to a staging host and restored into RDS from there. Nothing is written inside the database pod at any point, so the procedure works even when the existing storage volume has no free space for a dump file. The export stage is read-only against the source cluster, leaving the original environment available as a fallback until the migration is accepted. Object storage migration is a separate procedure; see Migrate Object Storage (MinIO to S3).Tools you will need
- AWS user with permissions to describe the RDS instance, modify its parameter group, and create snapshots.
-
Required utilities for the staging host:
- AWS CLI
-
kubectl, with access to the source cluster - MySQL client tools matching the source server version
-
A staging host with:
- Network access to the RDS endpoint, or the ability to transfer the dump file to a host that has it
- Free disk space for the compressed dump
Structure of this Article
The first section explains where the database migration belongs in the overall install sequence, because restoring at the wrong point produces a schema that cannot be repaired automatically. The remaining sections cover assessing the source database, preparing the RDS instance, exporting and restoring the data, reconciling the schema migration metadata, applying pending schema migrations, rebuilding the search index, and verifying the result.Installer privileges
All commands are run from a staging host by a user with the permissions stated in the “Tools” section above. Commands that target the source cluster assume the LILT namespace islilt and the database pod is mysql-0; adjust if your installation differs.
Migration Order
Restore before installing LILT
The application’s schema migrations are the mechanism that brings an older database up to the current schema. They must run against the restored data, which means the restore has to complete before LILT is installed on the new cluster. Installing first and restoring afterwards does not work, and fails in a way that is difficult to detect. On an empty database the installer applies every migration and produces a complete current-version schema. Restoring an older dump on top of that does not reset it: a dump drops and recreates only the tables it contains, so every table introduced after the dump was taken survives untouched, while the migration metadata table is replaced wholesale with the older list. The result is a mixture of old and new tables whose metadata claims the newer migrations never ran. Nothing reports an error at that point; the inconsistency surfaces later as failures during a subsequent upgrade. The correct sequence is:- Provision the RDS instance and prepare the parameter group
- Create the application database user and grants
- Restore the dump
- Reconcile the schema migration metadata
- Take an RDS snapshot
- Install LILT, which applies the pending schema migrations
- Reset the search index cursor and restart the indexer
- Verify, then return the environment to service
Migrate first, upgrade separately
If the new environment also runs a newer LILT release than the source, treat the storage move and the version upgrade as two operations. Restore the data and confirm it is intact at the original version first, then perform the version upgrade against RDS. Combined, a failure partway through several hundred schema migrations cannot be attributed to either cause.Assess the Source Database
Set the user environment variables
lilt_dev is the default application database name. Confirm it against your installation:
Read the database credentials
Record the server configuration
These values determine the export options and the RDS parameter group. Record all of them before continuing:Measure the database
This sizes the disk requirement on the staging host. A logical dump is smaller than the reported total because it contains no index data, and compresses further:Reclaim space on the storage volume
When binary logging is enabled, the log files are written into the data directory and can occupy a significant share of a small volume. Reclaiming that space reduces the risk of the volume filling during the export:Prepare the RDS Instance
Engine version
Create the instance with an engine version matching the source server recorded above. Restoring a dump taken from a newer server into an older engine version can fail on syntax or features the older version does not support.Parameter group
Set the following in the instance’s parameter group:log_bin_trust_function_creators is required because the restoring user does not hold the SUPER privilege on RDS, and stored routines or triggers in the dump fail without it.
If the application’s database client cannot negotiate the caching_sha2_password authentication plugin, also enable mysql_native_password in the parameter group. Recent MySQL versions disable that plugin by default, and the failure only appears when the application connects — testing as the administrative user succeeds regardless.
Correct the database character set and collation
This step is required when the database was created for you, either by the instance’s initial database name setting or by an infrastructure-as-code definition. A dump taken with the--databases option includes a CREATE DATABASE ... IF NOT EXISTS statement carrying the source character set and collation. When the database already exists, that statement does nothing and its collation clause is ignored, leaving the database on the engine’s default collation instead of the source’s.
The restored tables are unaffected, because every CREATE TABLE statement in the dump carries its own character set and collation. The problem appears later: tables created by subsequent schema migrations without an explicit collation inherit the database default, and queries joining them against restored tables fail with Illegal mix of collations. That surfaces days afterwards as intermittent errors rather than as a restore failure.
Create a credentials file for the administrative user and correct the collation to match the source:
ALTER DATABASE changes only the default applied to newly created objects; it does not rewrite existing data.
Create the application database user
User accounts and grants are not carried by the dump and must be created directly. Skip this step if your infrastructure definition already provisions the user:CREATE privilege it includes is what allows the dump to create the database if it is not already present.
Do not run any database bootstrap or schema initialization job at this stage. Creating the database and user is required; applying schema migrations before the restore produces the inconsistent schema described in “Migration Order”.
Export the Database
Stop application writes
A dump is only consistent if nothing is writing to the database. This also protects the storage volume: a consistent dump requires the server to retain previous row versions for the duration of the export, and on a volume with little free space a long export against a live database can exhaust it. Record the current replica counts first, so the environment can be restored if the migration is abandoned:Open a connection to the database
Forward the database service port to the staging host. Port13306 is used locally to avoid conflicting with any MySQL instance already running there:
REQUIRED encrypts the connection without validating the server certificate, which is normally what is wanted for an export running through a local tunnel:
127.0.0.1. Use ssl-mode=VERIFY_CA with ssl-ca pointing at the issuing authority if validation is required, and connect by a name the certificate covers rather than by address:
ssl-mode=DISABLED instead. Do not carry any of these settings over to the RDS credentials file: RDS requires encrypted connections and presents certificates from a public authority, so it keeps ssl-mode=REQUIRED as shown later in this article.
Port forwarding tunnels through the Kubernetes API server and can drop during a long transfer. If the connection proves unstable, capture the service definition, expose it as a NodePort for the duration of the migration, connect directly to a node, and restore the original definition afterwards:
Export to the staging host
The export runs on the staging host and writes only there. No dump file is created inside the database pod:-
--defaults-extra-filemust be the first argument on the command line, or it is ignored without warning. -
--set-gtid-purged=OFFis required when the source has GTID mode enabled. Without it the dump contains a statement that sets a global GTID variable, which RDS rejects because the restoring user does not holdSUPER. -
--no-tablespacesavoids a statement requiring thePROCESSprivilege, which the restoring user does not hold. -
--single-transactionproduces a consistent snapshot without locking tables. -
--databasesincludes theCREATE DATABASEandUSEstatements, making the dump self-contained. -
The
sedfilter removesDEFINERclauses, which reference user accounts that do not exist on the new instance. Applying it in the same pipeline avoids writing the data to disk twice.
Validate the export
A truncated dump that appears to restore successfully is the worst outcome, so validate before restoring:1 and the other two must be 0. The Dump completed marker is written only when the export finishes cleanly, so its absence means the connection dropped partway and the export must be repeated.
Record the row-count baseline
Capture this while writes are still stopped. It is the reference for verification after the restore:Close the connection
Restore into RDS
If the staging host cannot reach the RDS endpoint, transfer the dump to a host that can before continuing. Create a credentials file for the application user and restore:ssl-mode=REQUIRED. Expect the restore to take considerably longer than the export, because it rebuilds every index.
If the restore fails partway, do not retry over the partial result. Recreate the database with the correct collation and start again:
Reconcile the Schema Migration Metadata
The application records each applied schema migration by filename in a metadata table namedSequelizeMeta, and matches them by exact filename. Some migrations are authored with a .ts extension and shipped in the application image compiled to .js. If a database recorded them under their .ts names, the installed application sees those migrations as never applied, attempts to run them again, and fails on objects that already exist — typically reported as a duplicate key or duplicate column error.
Check whether the restored database is affected:
ts is 0, no action is needed. Otherwise normalize the recorded names to the compiled extension. This affects only the metadata table — no schema and no application data are modified — and running it a second time has no effect:
DELETE handles the case where both extensions are recorded for the same migration, which would otherwise collide on the table’s primary key during the rename.
Take a Snapshot
Create a snapshot before any schema migration runs. This is the only inexpensive way to recover from a migration that applies partially:Apply Schema Migrations
Install LILT on the new cluster as described in Install System (AWS EKS), with the database connection pointing at the RDS endpoint. During installation the application applies every schema migration recorded as unapplied, bringing the restored database up to the current schema. When migrating from an older release, expect a large number of migrations to run. This is normal and represents the accumulated schema changes between the two versions.Confirm the migrations succeeded
Do not infer success from pod status. A migration failure does not necessarily cause the pod to report a failure, so the application can start against a partially migrated schema. Inspect the initialization container’s logs directly:unreconciled must be 0. If it is not, the reconcile step was skipped or new entries were recorded under the wrong extension, and the next upgrade will attempt to reapply them.
Installation is idempotent with respect to migrations: any that did not run are applied on the next install or upgrade. If the logs show a failure, resolve the cause and run the installer again rather than editing the metadata table by hand.
Rebuild the Search Index
The search index on the new cluster is empty, and restored content is not indexed automatically. The indexer tracks its position in a table namedIndexerCursor, which holds a timestamp; only records modified after that timestamp are indexed. The restored row carries the position reached on the original cluster, and every restored record predates it, so without this step search returns no results for migrated content.
Perform this after the schema migrations have completed. The cursor is read once when the indexer starts, so it must be paired with a restart.
CURSOR is a reserved word in MySQL and must be quoted with backticks:
DATETIME, so a date before 1970 is also accepted; the value above is simply a conventional starting point. The search indices themselves are created automatically when the indexer starts and require no manual step.
Monitor progress by watching the index document counts grow:
Verify the Migration
Compare row counts
information_schema are estimates for InnoDB tables, so use this comparison to identify substantial differences and confirm those with a direct COUNT(*) on the largest tables. Counts will legitimately differ for tables that the schema migrations altered.
Confirm the character set and collation
Application checks
Perform these in order, because each one exercises a different part of the migration:- Sign in, which confirms the application can authenticate against the restored user records.
- Confirm the jobs list is populated, which confirms general read access to restored data.
- Open a previously existing document, which confirms the restored records resolve correctly.
- Search for a phrase from restored content, which confirms the index was rebuilt.
- Create a new job and run a translation, which confirms writes succeed against the new instance.
Post-Migration Configuration
- Connection settings. The database host, port, user and password must point at the RDS endpoint. Confirm every location where the deployment configures them.
- Application user. Confirm the application authenticates as the dedicated database user rather than the instance’s administrative user.
- In-cluster database. Once the migration is accepted, the in-cluster MySQL deployment is no longer used. Leave it in place until then, since it is the fallback.
- Encrypted connections. RDS requires TLS. Any component configured for an unencrypted in-cluster connection must be updated.
Cleanup
Restore the application to its previous replica counts using the file recorded before the export, then remove the credentials files and the staged dump:Debugging
Access denied when the application connects, while administrative access works: the application’s client cannot negotiate the instance’s default authentication plugin. Enable mysql_native_password in the parameter group and recreate the application user with that plugin.
Restore fails on a stored routine or trigger: log_bin_trust_function_creators is not set to 1 in the parameter group.
Restore fails on a statement setting a global GTID variable: the export was taken without --set-gtid-purged=OFF. Repeat the export with that option.
Illegal mix of collations after an upgrade: the database default collation does not match the restored tables, because the database was created before the restore and its collation was not corrected. Compare the database default against a restored table’s collation and correct it, then recreate the affected tables.
Duplicate key or duplicate column errors during schema migration: migration metadata records entries under a different file extension than the installed image uses. See “Reconcile the Schema Migration Metadata”.
Search returns no results for migrated content: the index cursor was not reset, or the indexer was not restarted after resetting it.
The export stops partway with a connection error: the port-forward tunnel dropped. The dump cannot be resumed; repeat the export, and use the NodePort approach if the tunnel proves unreliable.
