How to Connect to MariaDB from Python

6 min readMariaDB

MariaDB speaks the MySQL wire protocol, so you have an unusual choice: use MariaDB's own official connector, or use any of the MySQL drivers, which work fine against MariaDB. Plenty of production MariaDB deployments run on PyMySQL or mysqlclient and never touch the official connector. This guide covers when each choice makes sense, with working code for all of them.

The options

DriverMaintainerImplementationparamstyleVersion (as of June 2026)
mariadb (Connector/Python)MariaDBC extension over Connector/Cqmark (?)1.1.14 stable, 2.0 in RC
mysqlclientPyMySQL orgC extension over libmysqlclient/Connector/Cformat (%s)2.2.8
PyMySQLPyMySQL orgPure Pythonformat (%s)1.2.0
mysql-connector-pythonOraclePure Python (+ optional C)format (%s)9.7.0

Rules of thumb:

  • Greenfield MariaDB-only project: the official mariadb connector. It is the only driver that exposes MariaDB-specific capabilities directly and tracks MariaDB server releases.
  • Code that might run against MySQL too, or a team that already knows the MySQL drivers: mysqlclient (speed) or PyMySQL (zero build dependencies). Nothing breaks; you just write %s placeholders instead of ?.
  • Django: mysqlclient remains the documented default for MariaDB backends. Don't fight the framework.

The drivers are not drop-in compatible with each other -- the official connector uses ? placeholders (qmark) while every MySQL driver uses %s. Switching means touching every query. Pick once.

The official mariadb connector

The build dependency trap

Version 1.1 is a C extension that links against MariaDB Connector/C. On Windows, pip installs a prebuilt wheel. On Linux and macOS, there are no binary wheels for 1.1 -- pip compiles from source, which fails with a mariadb_config not found or Python.h: No such file or directory error unless the system packages are present:

# Debian/Ubuntu
sudo apt install libmariadb-dev python3-dev
pip install mariadb
 
# macOS
brew install mariadb-connector-c
pip install mariadb

This is the single most common installation failure, and it is the main reason teams quietly use PyMySQL instead. The upcoming 2.0 release fixes it: 2.0 (release candidate as of June 2026) is a ground-up rewrite distributed as pure Python with an optional C accelerator, installable via pip install mariadb[binary] with Connector/C bundled, and it adds native asyncio support. Until 2.0 goes stable, expect the apt/brew step on 1.1.

Connecting

import mariadb
 
conn = mariadb.connect(
    host="localhost",
    port=3306,
    user="app",
    password="secret",
    database="shop",
)
 
cur = conn.cursor()
cur.execute("SELECT id, name FROM customers WHERE country = ?", ("CH",))
for row in cur:
    print(row)
conn.close()

Note the ? placeholder -- qmark style, like SQLite, unlike every MySQL driver. Tuples in, tuples out. cur.execute(sql, ("CH",)) needs the trailing comma for single parameters; passing a bare string is the classic mistake (each character becomes a parameter).

Autocommit is off by default, matching the DB API spec. Your INSERTs vanish on close unless you call conn.commit() or set conn.autocommit = True.

Connection pooling

Built in, no extra package:

pool = mariadb.ConnectionPool(
    pool_name="app",
    pool_size=5,
    host="localhost",
    user="app",
    password="secret",
    database="shop",
)
 
conn = pool.get_connection()
try:
    cur = conn.cursor()
    cur.execute("SELECT 1")
finally:
    conn.close()  # returns the connection to the pool

MariaDB-specific: INSERT ... RETURNING

MariaDB 10.5+ supports RETURNING on INSERT (and DELETE since 10.0, REPLACE since 10.5) -- a feature MySQL does not have. Any driver can use it since it is just SQL, but it replaces the lastrowid dance cleanly:

cur.execute(
    "INSERT INTO orders (customer_id, total) VALUES (?, ?) RETURNING id, created_at",
    (42, 199.90),
)
order_id, created_at = cur.fetchone()

Using the MySQL drivers against MariaDB

Everything in our MySQL from Python guide applies to MariaDB: same connection parameters, same %s placeholders, same utf8mb4 charset advice, same wait_timeout reconnection traps. A minimal PyMySQL example for completeness:

import pymysql
 
conn = pymysql.connect(
    host="localhost",
    user="app",
    password="secret",
    database="shop",
    charset="utf8mb4",
    cursorclass=pymysql.cursors.DictCursor,
)
 
with conn.cursor() as cur:
    cur.execute("SELECT id, name FROM customers WHERE country = %s", ("CH",))
    print(cur.fetchall())
conn.commit()

Two MariaDB-vs-MySQL differences worth knowing when you reuse MySQL drivers:

  • Authentication plugins. MySQL 8 defaults to caching_sha2_password, which older drivers struggled with; MariaDB defaults to mysql_native_password (and offers ed25519). Connecting to MariaDB therefore avoids the auth-plugin errors common with MySQL 8 -- but if you followed MySQL hardening guides that force caching_sha2_password, note MariaDB does not implement it.
  • Version strings. MariaDB reports versions like 11.8.3-MariaDB. Code that parses server versions (some ORMs, migration tools) occasionally misreads this. SQLAlchemy handles it via dedicated mariadb dialect naming (below).

SQLAlchemy

SQLAlchemy 2.0 has explicit MariaDB-only dialect prefixes, which enable MariaDB-specific features (like INSERT...RETURNING support in the ORM) and refuse to connect to a MySQL server:

from sqlalchemy import create_engine
 
# Official mariadb connector
engine = create_engine("mariadb+mariadbconnector://app:secret@localhost/shop")
 
# Or via the MySQL drivers
engine = create_engine("mariadb+pymysql://app:secret@localhost/shop?charset=utf8mb4")

Add pool_pre_ping=True if connections sit idle long enough to hit the server's wait_timeout.

Async

As of June 2026 the stable official connector (1.1) has no asyncio support -- that arrives with 2.0. Today your async options are the MySQL ecosystem drivers, which work against MariaDB: asyncmy (0.2.11, C-accelerated) or aiomysql (built on PyMySQL). Both use %s placeholders and integrate with SQLAlchemy's async engine (mariadb+asyncmy:// is not a registered dialect, so async SQLAlchemy users connect with the mysql+asyncmy:// prefix and lose the MariaDB-only dialect features).

Common errors

ErrorCause
mariadb_config not found / Python.h: No such file during pip installMissing libmariadb-dev (or Connector/C) and python3-dev
Commands out of syncUnread result set; consume or use buffered cursors
Inserts silently missingAutocommit off (DB API default), no conn.commit()
Access denied for user 'app'@'172.x.x.x'MariaDB accounts are user@host patterns; the Docker bridge IP needs its own grant or 'app'@'%'
Server has gone away after idlewait_timeout closed the connection; use pool_pre_ping, conn.ping(), or auto_reconnect

Verifying your data

After connecting, you usually want to browse schemas and check rows landed. The mariadb CLI works; a GUI is faster -- we compared the options in Best MariaDB GUI Clients. For loading data, see Import CSV to MariaDB and Import Excel to MariaDB.

Mako connects to MariaDB with AI-powered autocomplete. Try it free at mako.ai.

Mako — open source

Skip the terminal. Use Mako.

Connect your database, write queries with AI assistance, and import/export data in clicks. Free to start.