---
title: "MySQL to MariaDB migration guide"
description: "Convert a MySQL dump into a MariaDB script that loads on the first try: what MariaDB doesn't accept, how to load it, and every rule the converter applies."
canonical: https://intosql.com/docs/migrations/mysql-to-mariadb
last_updated: 2026-10-15
---

# MySQL to MariaDB migration guide

IntoSQL reads a MySQL or MariaDB dump and writes one MariaDB script with the same tables, columns, keys, indexes, foreign keys, defaults, counters and rows. A MySQL 8 dump often fails on MariaDB as it is (the `utf8mb4_0900_ai_ci` collation, functional defaults, invisible columns); the converter rewrites what MariaDB doesn't accept and lists every change in the report. The script loads on MariaDB 10.11 and later.

**Convert in the browser:** [MySQL to MariaDB](/mysql-to-mariadb) opens the converter set up for this pair.

## Dump for the best result

Any dump from mysqldump, mariadb-dump, mysqlpump, phpMyAdmin or Adminer works, as `.sql` or `.sql.gz`. These options keep more of your data:

```shell
mysqldump --default-character-set=utf8mb4 --hex-blob \
  --routines --triggers --events --single-transaction \
  shop | gzip > shop.sql.gz
```

- `--default-character-set=utf8mb4` keeps emoji and other 4-byte characters. A dump made with `utf8` has already lost them, and the report says so (`dump_lost_four_byte_chars`).
- `--hex-blob` writes binary columns as hexadecimal, which survives copying between systems.
- `--routines --triggers --events` add those objects to the dump, so the report can list them by name.
- `--single-transaction` takes a consistent snapshot of InnoDB tables without locking them.

A dump made with `--no-create-info` still converts: the types are read from the values, and the report says the schema was missing (`no_schema`).

## Convert

In the browser, open [MySQL to MariaDB](/mysql-to-mariadb) or the converter on the home page, drop the dump and choose MariaDB. It reads the file from your disk in pieces, twice, and writes the script as it goes, so the dump is never uploaded and its size isn't limited by memory. The [desktop app](/docs/desktop) does the same with the file on disk.

With the [command line](/docs/cli):

```shell
intosql inspect shop.sql.gz
intosql convert shop.sql.gz --to mariadb --output shop.mariadb.sql --report shop.report.json
```

`inspect` lists the tables, their rows and what won't be converted, without writing anything. `convert` writes the script and, with `--report`, the report as JSON.

Through the [API](/docs/api), a dump converts like any other file: open a conversion with its name ending in `.sql` or `.sql.gz` and `"output": {"dialect": "mariadb"}`.

## Load into MariaDB

```shell
mariadb -e "CREATE DATABASE shop"
mariadb --default-character-set=utf8mb4 shop < shop.mariadb.sql
```

The script sets the character set, the time zone and a SQL mode that accepts the dump's zero and impossible dates, then creates each table with its keys and copies the rows with foreign key and unique checks off, as mysqldump does. Each `AUTO_INCREMENT` counter continues where the dump left it.

### Or load it straight in

`intosql load` and the [desktop app](/docs/desktop) load the dump into MariaDB without a script, with the same tables, keys and indexes the script creates:

```shell
intosql load shop.sql.gz --into mariadb://ana@localhost/shop
```

It stops before writing anything when one of the dump's tables already exists. MariaDB can't roll back a table, so a failed load drops the tables it created. Loading into a database server is part of IntoSQL Pro, with a 14-day trial. See [Loading](/docs/loading#database-dumps).

## What isn't converted

Views, triggers, stored procedures, functions and events are listed in the report by kind and name, never dropped silently, so you can check each one against MariaDB before recreating it. `ON UPDATE CURRENT_TIMESTAMP` stays as it is.


## Data types

[MySQL to MariaDB data types](/docs/types/mysql-to-mariadb) lists each type that changes in the script: what it becomes, why, and the report line.

## Every other rule

Generated from the engine, where each rule is decided.

| In MySQL | In MariaDB | Why | Report line |
| --- | --- | --- | --- |
| utf8mb4_0900_* and utf8mb4_uca1400_* collations | utf8mb4_unicode_520_ci for utf8mb4_0900_* (utf8mb4_bin for binary ones) | MariaDB doesn't know the newer collations, the error that stops most MySQL 8 dumps from loading elsewhere. unicode_520 follows the same Unicode rules most closely. | `collation_replaced` |
| ON UPDATE CURRENT_TIMESTAMP | kept | The same clause, with the column's fractions of a second. | None |
| generated columns | VIRTUAL or STORED as in the source | The same formula, translated term by term. | None |
| generated columns with functions IntoSQL doesn't read | a plain column | A formula IntoSQL can't read leaves a plain column and a report line. | `generated_not_translated` |
| INVISIBLE columns | INVISIBLE columns | MySQL has invisible columns from 8.0.23, MariaDB from 10.3. | None |
| CHECK constraints | kept | The same checks, translated term by term. | None |
| CHECK constraints with functions IntoSQL doesn't read | not created | A check IntoSQL can't read is left out and reported. | `check_skipped` |
| CHECK ... NOT ENFORCED | not created | MySQL 5.7 and MariaDB can't keep a check without applying it, and the rows may break it. | `check_not_enforced` |
| expression defaults, and defaults on TEXT, BLOB, JSON and GEOMETRY | kept | MySQL takes them from 8.0.13; the script loads on 5.7, so they are left out and reported. MariaDB takes them. | `default_not_translated` |
| functional indexes, KEY ((expr)) | not created | MySQL indexes expressions from 8.0.13 and MariaDB not at all; a generated column with an index does the same. | `index_skipped` |
| keys and indexes on a prefix, c(20) | kept | The same index. | None |
| FULLTEXT indexes | kept | The same index; MATCH ... AGAINST works as before. | None |
| SPATIAL indexes | kept | The same index. | None |
| foreign keys | kept, loaded with FOREIGN_KEY_CHECKS=0 | The same keys; rows load as they did from the source dump. | None |
| PARTITION BY | one table | The rows are the same; partition the table again if it needs it. | `partitions_dropped` |
| WITH SYSTEM VERSIONING (MariaDB) | WITH SYSTEM VERSIONING, starting from the current rows | A dump holds the current rows only; MySQL has no system versioning. | None |
| Aria, ColumnStore, CONNECT, Spider, RocksDB (MariaDB) | kept | MySQL has none of these engines. Every other engine is kept. | None |
| CREATE SEQUENCE (MariaDB) | CREATE SEQUENCE with its next value | MySQL has no sequences. | None |
| views, triggers, procedures, functions and events | listed in the report | They are listed for you to recreate; their code often names its definer and server features. | `not_converted_objects` |
| NOT NULL columns with values that become NULL | NOT NULL dropped | A column whose values had to become NULL can't keep NOT NULL, or the load would stop. | `not_null_relaxed` |
| text decoded twice (Ã±) | copied as it is | The dump holds exactly these characters, so they are copied; the report points them out. | `mojibake_suspected` |
| dumps without CREATE TABLE | types read from the values | A dump made with --no-create-info only has rows, so the types come from the values. | `no_schema` |
| SET NAMES utf8 from utf8mb4 tables | converted, with a warning | MySQL 5.x mysqldump writes 3-byte utf8 by default and replaces every emoji with a question mark before any converter sees the file. | `dump_lost_four_byte_chars` |
| dumps without SET NAMES whose text isn't UTF-8 | read as Latin-1 (Windows-1252) | MySQL's latin1 is Windows-1252, the usual encoding of such dumps. | `charset_guessed` |
| sjis, cp932, gbk, big5 dumps | read as UTF-8, with a warning | These encodings are not decoded yet. | `unsupported_charset` |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | evaluated | Some dump tools write values as these calls; they are read as the values they produce. | None |
| other expressions in rows | NULL | A converter that doesn't run a database can't evaluate them. | `expression_value` |
| DEFAULT in VALUES | the column's default value | A literal default is copied into the row. | `default_keyword_filled` |
| DEFAULT in VALUES without a literal default | NULL | There is no value to copy. | `default_keyword_null` |
| REPLACE and INSERT IGNORE | plain rows | A dump has one row per key, so the rows are the same. | `replace_as_insert` |
