The short answer
Pick MariaDB for WordPress and most PHP apps, and whenever the app just says "MySQL or MariaDB". In our test it wrote faster, restored a dump in half the time, has its physical backup tool in the same image, and has a non-profit foundation next to the company behind it.
Pick MySQL when the app says "MySQL 8 or newer" and means it: it uses SQL that only MySQL understands, such as ->> on JSON columns, or it needs Group Replication, InnoDB Cluster or MySQL's document store. MySQL read slightly faster, started with less memory, and its JSON is a real binary type.
What we tested
Both ran on the same Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13, set up exactly as in our guides: MySQL under the user mysql, and MariaDB under the user mariadb, each a single rootless Podman container with the official image. Both guides use the same memory settings: a 4 GB InnoDB buffer pool, a 1 GB redo log, 200 connections and 64 MB for temporary tables.
Both guides use port 3306, so for this test MySQL listened on 3307: one line, port = 3307, in its server.cnf, and -P 3307 in its health check. To keep the measurements fair, the benchmarks and restores ran with the other server stopped; the rest ran with both up and the other idle.
| MySQL | MariaDB | |
|---|---|---|
| Version | Community Server 9.7.2 (LTS) | 12.3.3 (LTS) |
| Install, following our guide | 49 seconds | 36 seconds |
| Containers | 1 | 1 |
| Image | 952 MB | 346 MB |
| Memory after a reboot, idle | 796 MB | 1,179 MB |
| Memory after the benchmarks | 3.1 GB | 2.8 GB |
| Data on disk, empty | 1.2 GB | 1.1 GB |
| Data on disk, after all tests | 8.4 GB, of which 5.1 GB binary log | 3.1 GB |
| Read-only, 16 clients (transactions/s) | 800 | 771 |
| Read-write, 16 clients | 359 | 499 |
| Write-only, 16 clients | 652 | 1,455 |
| Load 8 million rows (sysbench prepare) | 195 seconds | 124 seconds |
| Dump everything, our guide's script | 154 seconds | 162 seconds |
| Restore the 1.9 GB dump | 375 seconds | 177 seconds |
| Physical backup | 104 seconds, Percona XtraBackup, separate 1.56 GB image | 26 seconds, mariadb-backup, in the image |
Memory is the proportional set size (PSS) of mysqld and mariadbd. Disk is the data volume; both start at about 1 GB because both guides set a 1 GB redo log.
Speed
We used sysbench 1.0.20 from Debian on a 1.9 GB database, eight tables of a million rows, so everything fitted in the cache. After a two-minute warm-up, each test ran for 60 seconds, twice, at 1, 4, 16 and 64 clients. The table shows the average in transactions per second:
| Test | Clients | MySQL | MariaDB |
|---|---|---|---|
| Read-only | 1 | 352 | 304 |
| 4 | 838 | 724 | |
| 16 | 800 | 771 | |
| 64 | 732 | 731 | |
| Read-write | 1 | 101 | 111 |
| 4 | 235 | 399 | |
| 16 | 359 | 499 | |
| 64 | 431 | 525 | |
| Write-only | 1 | 198 | 438 |
| 4 | 463 | 1,040 | |
| 16 | 652 | 1,455 | |
| 64 | 1,127 | 2,016 |
MySQL read up to 15 % faster with few clients; at 64 they were equal. MariaDB was ahead at read-write from 4 clients, and about twice as fast at pure writes.
Most of the write difference is one default. MySQL keeps a binary log of every change, written to disk at each commit, which replication and restoring to a point in time use. MariaDB has it off until you switch it on. We ran the write tests again with that swapped, at 16 clients:
| MySQL | MariaDB | |
|---|---|---|
| Both without binary log, read-write | 506 | 499 |
| Both without binary log, write-only | 1,027 | 1,455 |
| Both with binary log, read-write | 359 | 442 |
| Both with binary log, write-only | 652 | 1,013 |
Without it on both, read-write was even; with it on both, MariaDB was 23 % ahead. At pure writes, MariaDB stayed 40 to 55 % ahead either way. If you need the binary log, for a replica or for point-in-time recovery, switch it on in MariaDB and count on that speed, not the higher one.
The same binary log made MySQL slower at loading data: restoring the dump took 375 seconds against MariaDB's 177, and loading the test data 195 against 124.
Setup and day-to-day use
Both guides are the same length and the same shape, and both servers came back by themselves after a reboot. The differences:
- MySQL's image is almost three times larger, 952 MB against 346 MB. It also holds MySQL Shell.
- MySQL needs one extra setting in our guide,
net.ipv4.ip_nonlocal_bind, to start before WireGuard at boot. MariaDB listens on the tunnel address even before it exists. - MySQL also listens on port 33060 for its X Protocol unless you switch it off, as our guide does.
- The client. Debian has no MySQL client from Oracle. Its
mysqlcommand comes from MariaDB, and it worked with both servers, over TLS too. - Characters inside the container. In the MySQL image,
podman exec mysql mysqltalkslatin1, since the image has no locale. A name with "Å" typed or piped that way was stored garbled. Dumps are not affected, since they set the character set themselves; for anything else, add--default-character-set=utf8mb4. MariaDB's client usedutf8mb4.
Memory went the other way from what the images suggest: after a reboot, MariaDB used 1.2 GB and MySQL 800 MB, since MariaDB sets up part of its cache when it starts. Under load both grew towards the 4 GB we gave them.
Do not run both on one 8 GB server with 4 GB of cache each. We did, and when both had filled their caches, a restore into MySQL stalled the whole server for minutes while the kernel looked for memory. Run one per server, as the guides say, or halve both caches.
The same app on both
We ran one app's schema and queries through Python's PyMySQL on both: a customers table and an orders table with a foreign key, a CHECK, a JSON column, and a column generated from the JSON with an index. These worked the same on both:
- foreign keys,
CHECKconstraints and generated columns, which refused the same bad rows; JSON_EXTRACT,JSON_UNQUOTE,JSON_SET,JSON_CONTAINS,JSON_TABLE,JSON_OBJECT,JSON_ARRAYAGGandJSON_SCHEMA_VALID;- window functions, recursive
WITH,EXCEPT,REGEXP_REPLACE,GROUP_CONCAT; INSERT ... ON DUPLICATE KEY UPDATEwithVALUES();'anna astrom'finding "Anna Åström", since both compare text without case and accents by default.
These only worked in MySQL, and MariaDB answered with a syntax error:
meta->>'$.sku'andmeta->'$.tags', MySQL's short form forJSON_EXTRACT;INSERT ... AS new ON DUPLICATE KEY UPDATE name = new.name;LATERALsubqueries;MEMBER OF, and indexes on JSON arrays (CAST(... AS CHAR(20) ARRAY)).
And these only in MariaDB: INSERT ... RETURNING, sequences and tables that keep their own history (below).
WordPress 7.1.3 installed with wp-cli on both, and creating posts and comments and a search-and-replace of the site address worked the same. WordPress recommends MySQL 8.0 or MariaDB 10.11 or newer, so both are well inside that.
JSON
In MySQL, JSON is a type of its own, stored in a binary format. In MariaDB, JSON is another name for LONGTEXT, with a check that the text is valid JSON:
`doc` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`doc`))
Both refused 'not json'. The difference shows in what you get back. We stored {"b":1, "a":2, "a":3}, with a duplicate key, in both:
| MySQL | MariaDB | |
|---|---|---|
| Stored and returned | {"a": 3, "b": 1} | {"b":1, "a":2, "a":3} |
JSON_EXTRACT(doc, '$.a') | 3 | 2 |
MySQL sorts the keys, removes duplicates keeping the last, and normalises spaces. MariaDB keeps the text as written. For apps that write JSON and read it back through the JSON functions, both work. Apps that compare JSON as text, or rely on MySQL's ->>, notice.
Features where they have parted ways
We tried each on its own server:
- Sequences (MariaDB):
CREATE SEQUENCE invoice_no START WITH 1000andNEXTVAL()gave 1000, 1001. MySQL has no sequences; useAUTO_INCREMENT. - Tables with history (MariaDB): a table
WITH SYSTEM VERSIONINGkept the old price after an update, andFOR SYSTEM_TIME AS OFshowed the table as it was a second earlier. MySQL has nothing like it. - Galera (MariaDB): the library for multi-primary clusters is in the image, ready for
wsrep_on. We did not build a cluster, which takes three servers. - Spider (MariaDB), the engine that spreads a table over several servers, is not in the official image:
INSTALL SONAME 'ha_spider'found no file. - Group Replication and InnoDB Cluster (MySQL): the
group_replicationplugin installed, and MySQL Shell 9.7.1 is in the image. We did not build a cluster either. - Document store (MySQL): with the X Protocol switched on for the test, MySQL Shell added JSON documents to a collection and searched them, with no SQL. MySQL stores the collection as a table with a JSON column.
- JSON duality views (MySQL): a view that shows rows as JSON documents, and inserts and updates through it, worked in the Community version.
- Thread pool: MySQL 9.7.2 Community loaded its
thread_poolplugin withplugin_load_add, though Oracle's page still lists Thread Pool under Enterprise Edition. MariaDB has one built in:thread_handling = pool-of-threadsswitched it on. - Vectors: both have a vector type and can compare vectors, with different function names.
Accounts and passwords
New accounts get different password methods:
- MySQL:
caching_sha2_password. The oldermysql_native_passwordis not loaded in 9.7: creating an account with it failed withPlugin 'mysql_native_password' is not loaded. - MariaDB:
mysql_native_password, the older method based on SHA-1. MariaDB 12.3 also hascaching_sha2_password, as a plugin you install; an account made with it logged in over TLS. Ed25519 and PARSEC, MariaDB's stronger methods, are in the image too.
Every client we used, the MariaDB client, PyMySQL and PHP's mysqlnd, worked with both. Since MySQL 9.7 does not load mysql_native_password, an old client that only knows that method cannot log in to it.
Accounts do not move between them with their passwords. MySQL's password hashes did not work in MariaDB, even with the same method; create the accounts again with their passwords.
Moving and replicating between them
A dump from MySQL into MariaDB loaded, apart from the index on a JSON array, which MariaDB does not have. MariaDB accepted MySQL's default collation, utf8mb4_0900_ai_ci, and turned the JSON columns into its LONGTEXT with a check.
A dump from MariaDB into MySQL did not load. MySQL did not know MariaDB's default collation, utf8mb4_uca1400_ai_ci. After we replaced it with utf8mb4_0900_ai_ci, MySQL still refused the rows of the orders table, because mariadb-dump writes the values of generated columns, and refused the table with history.
Replication from MySQL to MariaDB worked until the first JSON column. MariaDB connected as a replica over TLS, with a caching_sha2_password account, and copied new databases, tables, inserts, updates and deletes. The first row with JSON stopped it: Slave received unknown field type field 245, MySQL's binary JSON. Without JSON columns, it could carry you through a move from MySQL to MariaDB.
Replication from MariaDB to MySQL did not work. MySQL has global transaction IDs (GTIDs) on by default in this image, and refused a source without them. With ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS = LOCAL, it connected, but stopped at once with Found invalid event in binary log. The two GTID formats, uuid:1-870565 in MySQL and 0-2-119448 in MariaDB, have nothing in common.
Updates and backups
Updates. Both images update their bug and security fixes with podman auto-update. MySQL's LTS releases are supported for 5 years, plus 3 of extended support, and the next one comes after about two years. 9.7 is the last with that numbering; the next use the year and month, such as 26.7. MariaDB makes a long-term release every year, supported for three years: 12.3, from May 2026, until June 2029. Both upgraded in our guides by changing the image tag, MySQL from 8.4 and MariaDB from 11.8.
Dumps took the same time, about two and a half minutes for everything with our guides' scripts, and gave the same size, 768 MB compressed. Restoring was where MySQL was slower, as above. MySQL keeps 30 days of binary log by default; our guide keeps seven, and in our tests it still grew to 5.1 GB, more than the data itself. Count on the disk for it.
Physical backups, which copy the data files for fast restores of large databases, differ the most. mariadb-backup is part of MariaDB's image and took 26 seconds. MySQL's own tool, MySQL Enterprise Backup, is in the paid Enterprise Edition, so our guide uses Percona XtraBackup: a separate 1.56 GB image, 104 to 112 seconds for the same data, and for 9.7 only a release candidate, 9.7.1-rc1, when we tested.
Licence and cost
Both servers are open source under the GPL version 2 and cost nothing. Who stands behind them differs, as their own pages said when we checked on 14 October 2026.
MySQL belongs to Oracle, code and trademark. Oracle sells the same server under a commercial licence too, for products that include it, and sells MySQL Enterprise Edition, with Enterprise Backup, encryption of data on disk, a firewall, audit logging, data masking and support around the clock. Its page shows no prices. In February 2026, Percona and others in the MySQL community published an open letter asking Oracle to talk about a foundation for MySQL; Percona's letter says Oracle cut MySQL's staff by about half in autumn 2025.
MariaDB is developed mostly by the company MariaDB plc, which an investment firm, K1, took private in 2024, together with the MariaDB Foundation, a non-profit funded by sponsors. The Foundation's page says that MariaDB Server "will remain Free and Open Source Software licensed under GPLv2, independent of any commercial entities". Not everything from the company is open source: its database proxy, MaxScale, uses the Business Source License, free in production for fewer than three servers. The company also sells MariaDB Enterprise Server and support.
Neither choice locks you in to paying anything. With MariaDB, more of what you might need later, such as a physical backup tool, a thread pool and a cluster library, is in the free version.
Which one to pick
- MariaDB, for WordPress and most PHP apps, for apps that list both, and when you write a lot or want backups from the same image. Start with Run a MariaDB database server with rootless Podman. A VC-P Alloy runs it with 4 GB of cache.
- MySQL, for apps that require MySQL 8 or newer, or use MySQL's JSON syntax or features, and when you want Group Replication or the document store. Start with Run a MySQL database server with rootless Podman. A VC-P Alloy runs it with 4 GB of cache.
Check what your app says first: "MySQL 5.7 or MariaDB 10.x" means either works. If you are unsure, test the app's own setup against both; it takes a few minutes with these guides. Moving our test app from MySQL to MariaDB was a dump and a restore, while the other way needed the dump fixed by hand.