DuckDB
DuckDB embedded as a MariaDB storage engine for analytical workloads. Experimental — alpha maturity, not in mainline MariaDB releases.
Experimental — for evaluation only. The DuckDB storage engine is at alpha maturity. It is not yet merged into MariaDB Server main, not built into MariaDB releases, and not signed in any official package. Interfaces, configuration variables, behavior, and supported operations are subject to change. Do not use in production.
The DuckDB storage engine embeds DuckDB — a columnar, vectorized, in-process analytical database engine — inside MariaDB Server as a loadable plugin (ha_duckdb.so). Tables created with ENGINE=DuckDB store data in DuckDB's native columnar format, and queries against them are executed by the DuckDB engine. Cross-engine joins between DuckDB and other engines (e.g. InnoDB) are supported in a single SELECT.
The engine borrows its design from Alibaba's AliSQL DuckDB integration. The original code was heavily refactored to use MariaDB's handler API and plugin system, and the engine links against vanilla upstream DuckDB.
Use Cases
HTAP (Hybrid Transactional/Analytical Processing) — InnoDB serves the transactional workload, DuckDB serves analytics, both in the same MariaDB server.
Ad-hoc analytical queries — joins, aggregations, and window functions over large datasets without exporting data to a separate system.
Eliminating ETL — the analytical engine runs in-process; no separate cluster or data-movement pipeline.
Architecture
DuckDB 1.5.2 is statically linked into a single plugin shared library (ha_duckdb.so) along with the built-in ICU and JSON extensions. The engine integrates with MariaDB via the standard handler API and adds a select_handler for full query pushdown.
Cross-engine queries (a single SELECT joining a DuckDB table with, for example, an InnoDB table) are handled as follows: the entire query is pushed down to DuckDB. When DuckDB references a non-DuckDB table, a callback re-enters the MariaDB query pipeline through a cooperative fiber to stream rows back. The MariaDB optimizer still chooses the access path on the non-DuckDB side, and DuckDB's optimizer drives join order, hashing, and aggregation.
Supported Platforms
OS: Linux only.
Architectures:
x86_64andaarch64(ARM64).Distributions (auto-detected by the build script): Ubuntu 22.04, Ubuntu 24.04, Debian 12, Rocky Linux 8, Rocky Linux 9. Fedora 42 builds are produced by the MariaDB CI.
Compiler: GCC 12 or later (C++17 required).
MariaDB Server: the engine is being prepared for MariaDB 11.4. A merge into the mainline source tree and the shipping version are not yet final.
Installing
The engine is not included in MariaDB releases. There are two ways to obtain it: prebuilt test packages from MariaDB CI, or building from source.
Option 1: Prebuilt Test Packages
MariaDB CI builds the engine for several Linux distributions and publishes the artifacts on ci.mariadb.org. Each CI run has its own job number and a subdirectory per distribution and architecture, for example amd64-ubuntu-2404-deb-autobake/ or aarch64-ubuntu-2404-deb-autobake/.
These packages are unsigned and intended for testing only.
The CI job number is not stable across rebuilds. For the current job URL, see the blog posts under See also.
Example for Ubuntu 24.04 (amd64), with a current job number <JOB>:
Option 2: Build From Source
The engine integration lives on the 11.4 branch of MariaDB/server. The engine source resides in the server repository under storage/duckdb/; a helper script there handles dependency installation and the build.
On Rocky Linux 8, pass -R to use gcc-toolset-12:
Enabling the Plugin
The DuckDB plugin is not loaded by default. Add a configuration file to the server's drop-in directory:
Debian / Ubuntu:
/etc/mysql/mariadb.conf.d/duckdb.cnfRocky / Fedora / openSUSE:
/etc/my.cnf.d/duckdb.cnf
Each line is significant:
plugin-maturity=alphais required. The server's default maturity threshold rejects alpha plugins; this line is the explicit opt-in.plugin-load-add=ha_duckdb.soloads the engine at startup.duckdb-memory-limitsets the cap on memory DuckDB itself may use. The default (1 GiB) is too low for most analytical workloads — raise it to reflect the host's available RAM.
Restart the server, then verify the engine is registered:
The output should include a DuckDB row with Support: YES.
Verifying the Install
DuckDB requires UTF-8: specify DEFAULT CHARSET=utf8mb4 (or another UTF-8 charset) on every DuckDB table. Non-UTF8 charsets fall back to binary comparison.
Supported Operations
DDL:
CREATE TABLE,DROP TABLE,ALTER TABLE,RENAME TABLE.ALTER TABLE ... ENGINE=DuckDBmigrates an existing table into DuckDB storage.INSERT: all major MariaDB column types.SELECT: full pushdown to DuckDB, including aggregations, joins, window functions,GROUP BY, andORDER BY.UPDATEandDELETE: translated to DuckDB SQL.Cross-engine
JOIN: a singleSELECTcan join DuckDB tables with InnoDB (or other) tables. Example:User-defined functions: DuckDB-side UDFs are registered with MariaDB.
Configuration Variables
The plugin adds a family of duckdb_* server variables. The most commonly tuned ones:
duckdb_memory_limit
integer (bytes)
1073741824 (1 GiB)
Maximum memory DuckDB may use. Raise for analytical workloads.
duckdb_max_threads
integer
(auto)
Maximum DuckDB execution threads.
duckdb_temp_directory
string
(auto)
Directory used by DuckDB for spilling.
duckdb_require_primary_key
boolean
ON
Reject CREATE TABLE without a PRIMARY KEY.
duckdb_max_temp_directory_size
integer (bytes)
(unlimited)
Cap on temp-directory usage.
Additional tuning and debugging variables (batching, log categories, optimizer hints, EXPLAIN verbosity, etc.) are exposed by the plugin. List the full current set with:
Limitations
The engine is at alpha maturity. Known limitations at the time of writing:
No
AUTO_INCREMENTand noUUIDdefault-value generation.DECIMALprecision capped at 38 — DuckDB's maximum. Wider MariaDBDECIMALcolumns fail at DDL conversion.Strict
GROUP BY— DuckDB rejectsSELECTcolumns that are neither in theGROUP BYclause nor aggregated, even when MariaDB'ssql_modewould allow it.No XA
PREPARE.UTF-8 only — non-UTF8 charsets fall back to binary comparison. UCA-based MariaDB collations are approximated using DuckDB's
NOCASE/NOACCENTcollations.Some MariaDB functions are not pushdown-compatible — for example,
GROUP_CONCAT(),DATE_FORMAT(),JSON_CONTAINS(),FOUND_ROWS(), andLAST_INSERT_ID()have no DuckDB equivalent or differ in syntax. Such queries fall back to MariaDB-side evaluation.ALTER COLUMN DROP DEFAULTisn't propagated to the DuckDB catalog.TIMESTAMPis stored asTIMESTAMPTZ— keep MariaDB and DuckDB timezone settings consistent to avoid value shifts.Cross-engine scan is single-threaded — only the DuckDB side of a cross-engine query is parallelized.
Cross-engine
INSERT(DuckDB → InnoDB) has restrictions; see the engine compatibility matrix.
A maintained compatibility matrix is published in the server repository at storage/duckdb/docs/mariadb-duckdb-incompatibilities.md.
See Also
MDEV-39234 — upstream feature ticket.
11.4branch — the MariaDB Server branch where the engine source resides, understorage/duckdb/.DuckDB Storage Engine for MariaDB: When the Sea Lion Learns to Quack — Roman Nozdrin, 2026-06-09.
MariaDB DuckDB: A New Playground for Analytics — Frédéric Descamps, 2026-06-12.
Last updated
Was this helpful?

