A Python CLI for transferring SQLite 3 schema and data to MySQL or MariaDB.
sqlite3mysql reads the source schema from SQLite, creates equivalent MySQL/MariaDB tables, indexes, foreign keys, and
views where possible, then transfers table data into the target database.
- Python 3.9 or newer, unless you use the Docker image.
- A readable SQLite 3 database file.
- A reachable MySQL or MariaDB server.
- A MySQL user that can connect, create the target database when it does not exist, create tables and views, insert data, add indexes, and add foreign keys.
See the GitHub Actions CI matrix for the current MySQL and MariaDB versions tested by the project. Very old server versions are more likely to differ in type, default-value, JSON, FULLTEXT, or authentication behavior.
Install from PyPI:
pip install sqlite3-to-mysql
sqlite3mysql --helpOn macOS, you can also install with Homebrew:
brew install sqlite3-to-mysql
sqlite3mysql --helpOr run the published Docker image:
docker run --rm ghcr.io/techouse/sqlite3-to-mysql:latest --helpThis repo includes an optional agent skill at
skills/sqlite3-to-mysql/ for users who want Codex or another compatible agent to help prepare a safe sqlite3mysql transfer command. The skill is user-facing: it focuses on migration planning, CLI recipes, password-safe defaults, and MySQL/MariaDB caveats.
Use -p / --prompt-mysql-password for interactive password entry. This avoids putting the password in shell history
or process listings.
sqlite3mysql \
--sqlite-file ./app.sqlite3 \
--mysql-database app_db \
--mysql-user app_user \
--prompt-mysql-password \
--mysql-host 127.0.0.1 \
--mysql-port 3306Short options are equivalent:
sqlite3mysql -f ./app.sqlite3 -d app_db -u app_user -p -h 127.0.0.1 -P 3306For automation, --mysql-password is available, but prefer a secret manager or environment-expanded value rather than
typing the password directly into your shell history. The password is still passed as a command-line argument and may be
visible through process listings or logs.
Use host.docker.internal when the MySQL or MariaDB server is running on the host machine and the Docker container needs
to reach it. On Linux Docker Engine, add --add-host=host.docker.internal:host-gateway before the image name if
host.docker.internal is not resolvable.
docker run -it \
--rm \
--workdir "$PWD" \
--volume "$PWD:$PWD" \
ghcr.io/techouse/sqlite3-to-mysql:latest \
-f ./app.sqlite3 \
-d app_db \
-u app_user \
-p \
-h host.docker.internalFiles inside the mounted working directory are shared with the host.
Create the MySQL tables, indexes, views, and foreign keys without transferring table rows.
sqlite3mysql -f ./schema.sqlite3 -d app_db -u app_user -p --mysql-skip-transfer-data--mysql-skip-create-tables skips DDL creation and only inserts data. The MySQL tables must already exist and be
compatible with the SQLite source schema.
sqlite3mysql -f ./app.sqlite3 -d app_db -u app_user -p --mysql-skip-create-tablesTable names are space-separated and are consumed until the next CLI option.
sqlite3mysql -f ./subset.sqlite3 -d app_db -u app_user -p --sqlite-tables users orders invoicesTransfer everything except selected tables:
sqlite3mysql -f ./subset.sqlite3 -d app_db -u app_user -p --exclude-sqlite-tables audit_log temp_importsSelecting or excluding tables disables foreign key transfer because the referenced tables may not be present.
--mysql-truncate-tables deletes rows from matching target tables before inserting data.
sqlite3mysql -f ./app.sqlite3 -d app_db -u app_user -p --mysql-truncate-tablesUse --mysql-insert-method UPDATE to update existing rows when inserts hit duplicate keys.
sqlite3mysql -f ./app.sqlite3 -d app_db -u app_user -p --mysql-insert-method UPDATEUse a socket instead of TCP when the MySQL server is local and configured for socket connections.
sqlite3mysql -f ./app.sqlite3 -d app_db -u app_user -p --mysql-socket /var/run/mysqld/mysqld.sockVerify the server certificate with a CA file:
sqlite3mysql -f ./app.sqlite3 -d app_db -u app_user -p --mysql-ssl-ca /path/to/ca.pemUse a client certificate and key:
sqlite3mysql \
-f ./app.sqlite3 \
-d app_db \
-u app_user \
-p \
--mysql-ssl-ca /path/to/ca.pem \
--mysql-ssl-cert /path/to/client-cert.pem \
--mysql-ssl-key /path/to/client-key.pemUse --skip-ssl only when you explicitly need to disable MySQL connection encryption.
Use --chunk to tune the number of SQLite rows read at a time. Use --quiet to suppress progress output and
--log-file to write logs to a file.
sqlite3mysql -f ./app.sqlite3 -d app_db -u app_user -p --chunk 50000 --quiet --log-file transfer.log| Option | Purpose |
|---|---|
-f, --sqlite-file PATH |
Source SQLite database file. Required. |
-d, --mysql-database TEXT |
Target MySQL/MariaDB database name. Required. |
-u, --mysql-user TEXT |
MySQL/MariaDB user. Required. |
-p, --prompt-mysql-password |
Prompt for the MySQL password. Preferred for interactive use. |
--mysql-password TEXT |
Provide the MySQL password directly. Useful for automation, but handle carefully. |
-h, --mysql-host TEXT |
MySQL host. Defaults to localhost. |
-P, --mysql-port INTEGER |
MySQL port. Defaults to 3306. |
-k, --mysql-socket PATH |
MySQL Unix socket path. Cannot be combined with SSL certificate options. |
-t, --sqlite-tables TUPLE |
Transfer only the listed tables. Implies no foreign key transfer. |
-e, --exclude-sqlite-tables TUPLE |
Transfer every table except the listed tables. Implies no foreign key transfer. |
-A, --sqlite-views-as-tables |
Materialize SQLite views as MySQL tables instead of creating MySQL views. |
-X, --without-foreign-keys |
Do not create foreign keys in MySQL. |
-W, --ignore-duplicate-keys |
Skip duplicate SQLite index names instead of renaming them with a numeric suffix. |
-E, --mysql-truncate-tables |
Truncate matching target tables before inserting data. |
-K, --mysql-skip-create-tables |
Skip table/view creation and transfer data only. |
-J, --mysql-skip-transfer-data |
Create schema only and skip table data. |
-i, --mysql-insert-method [DEFAULT|IGNORE|UPDATE] |
Choose duplicate-row insert behavior. Defaults to IGNORE. |
--mysql-integer-type TEXT |
MySQL default integer column type. Defaults to INT(11). |
--mysql-string-type TEXT |
MySQL default string column type. Defaults to VARCHAR(255). |
--mysql-text-type [LONGTEXT|MEDIUMTEXT|TEXT|TINYTEXT] |
MySQL default text column type. Defaults to TEXT. |
--mysql-charset TEXT |
MySQL database and table character set. Defaults to utf8mb4. |
--mysql-collation TEXT |
MySQL database and table collation. Must belong to the selected charset. |
--mysql-ssl-ca PATH |
Path to an SSL CA certificate file. |
--mysql-ssl-cert PATH |
Path to an SSL client certificate file. Must be paired with --mysql-ssl-key. |
--mysql-ssl-key PATH |
Path to an SSL client key file. Must be paired with --mysql-ssl-cert. |
-S, --skip-ssl |
Disable MySQL connection encryption. Cannot be used with SSL certificate options. |
-T, --use-fulltext |
Use FULLTEXT indexes on text columns when the target server supports InnoDB FULLTEXT. |
--with-rowid |
Transfer SQLite rowid columns for tables that have rowids. |
-c, --chunk INTEGER |
Read and write SQL records in batches. |
-l, --log-file PATH |
Write logs to a file. |
-q, --quiet |
Show only errors after the initial command banner. |
--debug |
Re-raise exceptions for debugging instead of printing friendly errors. |
--version |
Show environment and dependency versions. |
--help |
Show CLI help. |
--sqlite-tablesand--exclude-sqlite-tablesare mutually exclusive.- Either table filter (
--sqlite-tablesor--exclude-sqlite-tables) automatically disables foreign key transfer. --mysql-skip-create-tablesand--mysql-skip-transfer-datacannot be used together because there would be nothing to do.--mysql-skip-create-tablesrequires compatible target MySQL tables to already exist.--mysql-truncate-tablesdeletes rows from matching target tables before inserting data.--mysql-socketcannot be combined with--mysql-ssl-ca,--mysql-ssl-cert, or--mysql-ssl-key.--skip-sslcannot be combined with--mysql-ssl-ca,--mysql-ssl-cert, or--mysql-ssl-key.--mysql-ssl-certand--mysql-ssl-keymust be provided together.--mysql-collationmust be valid for the selected--mysql-charset.--use-fulltextfails before transfer starts when the target server does not support InnoDB FULLTEXT indexes.- Native MySQL views are created by default. If a target table has the same name as a SQLite view, that target table is
dropped before the MySQL view is created. Use
--sqlite-views-as-tablesfor the older materialized-table behavior. - Table, view, column, index, and constraint names are truncated to MySQL's 64-character identifier limit.
--mysql-passwordpasses the password as a command-line argument. Prefer--prompt-mysql-passwordfor interactive use and inject secrets through a secret manager or CI secret store for automation.
MySQL SSL note: when --mysql-ssl-ca is provided, MySQL Connector/Python verifies the server certificate chain.
--mysql-ssl-cert and --mysql-ssl-key enable client certificate authentication. These options do not enable hostname
identity verification. If you provide only the client certificate and key without --mysql-ssl-ca, the server
certificate is not verified.
- MySQL and MariaDB are similar but not identical. JSON behavior, expression defaults, duplicate-key update SQL, timestamp defaults, fractional seconds, and FULLTEXT support can differ by server family and version.
- SQLite
JSONBmaps to MySQL/MariaDBJSONonly when the target supports JSON: MySQL>= 5.7.8and MariaDB>= 10.2.7. Otherwise it maps to the configured text type. - SQLite
JSONBvalue conversion requires SQLite 3.45 or newer. With older SQLite versions, JSONB columns can still be selected, but the SQLitejson()conversion function is not used during transfer. --mysql-insert-method UPDATEuses the MySQLVALUES (...) AS __new__alias only on MySQL>= 8.0.19; MariaDB keeps the older duplicate-key update form.- Expression defaults are supported on MySQL
>= 8.0.13and MariaDB>= 10.2.0; older servers may require defaults to be omitted or simplified. CURRENT_TIMESTAMPdefaults onDATETIMErequire MySQL>= 5.6.5or MariaDB>= 10.0.1.- Fractional seconds require MySQL
>= 5.6.4or MariaDB>= 10.1.2. - InnoDB FULLTEXT indexes require MySQL
>= 5.6.0or MariaDB>= 10.0.5. - After transfer, verify schema details that are important to your application, especially defaults, collations, JSONB columns, views, and foreign keys.
SQLite3 to MySQL is released under the MIT License.