TechEarl

How to Run MariaDB in Docker (With Persistent Storage)

MariaDB in a container with a named volume so your data survives container removal. The differences from MySQL's official image, the env-var compatibility quirk, and connecting from the host or another container.

Ishan Karunaratne⏱️ 7 min readUpdated
Share thisCopied
Run MariaDB in Docker with a named volume so your data persists. Covers MARIADB_* vs MYSQL_* env vars, version choice, connecting from the host, and switching from MySQL.

MariaDB is the community fork of MySQL and a drop-in replacement for many MySQL use cases — same wire protocol, same SQL surface for most queries, similar client tools. Running it in Docker is the same shape as running MySQL: one command for the basic case, plus a named volume for persistence so your data doesn't vanish with the container.

How do I run MariaDB in Docker?

bash
docker run -d --name mariadb \
  -e MARIADB_ROOT_PASSWORD=change-me \
  -p 3306:3306 \
  -v mariadb-data:/var/lib/mysql \
  mariadb:11.4

MariaDB 11.4 LTS in the background, root password set, port 3306 published, and a named volume on /var/lib/mysql so the data survives. (Yes, the volume path is /var/lib/mysql — MariaDB inherits the MySQL data-directory convention.)

Try it with your own values

Configure the version, ports, password, and volume.

Pick a version

  • mariadb:11.4 — MariaDB 11.4 LTS. Released May 2024, supported through May 2029. The default for new projects.
  • mariadb:10.11 — 10.11 LTS, supported through February 2028. Still common in production.
  • mariadb:10.6 — older LTS, supported through July 2026. Use only for matching existing infrastructure.

Tags also exist for non-LTS releases (mariadb:11.5, etc.) — short support windows, useful only if you specifically need a feature in those.

The basic run command

bash
docker run -d --name :container_name 
-e MARIADB_ROOT_PASSWORD=:root_password 
-p :host_port:3306 
-v :volume:/var/lib/mysql 
mariadb::mariadb_version

Optional init env vars (only honored on first run, when the data directory is empty):

bash
docker run -d --name :container_name 
-e MARIADB_ROOT_PASSWORD=:root_password 
-e MARIADB_DATABASE=myapp 
-e MARIADB_USER=appuser 
-e MARIADB_PASSWORD=apppass 
-p :host_port:3306 
-v :volume:/var/lib/mysql 
mariadb::mariadb_version

MARIADB_* vs MYSQL_* env vars

The MariaDB image accepts both MARIADB_* and MYSQL_* env vars for compatibility with apps and Compose files that originally targeted MySQL. So all of these work:

bash
-e MARIADB_ROOT_PASSWORD=...
-e MYSQL_ROOT_PASSWORD=...      # equivalent

-e MARIADB_DATABASE=myapp
-e MYSQL_DATABASE=myapp         # equivalent

If both are set, the MARIADB_* variant wins. New code should use MARIADB_*; the MYSQL_* aliases exist for "switching the image from mysql to mariadb without rewriting our Compose file."

Why you need a volume

Same reason as MySQL: the container's writable layer is destroyed when the container is removed. The data directory at /var/lib/mysql lives in that writable layer unless you mount a volume there. With -v :volume:/var/lib/mysql, the data lives in Docker-managed storage and survives container removal.

bash
Stop and remove the containerdocker stop :container_name && docker rm :container_nameStart a new MariaDB against the same volume — your data is still theredocker run -d --name :container_name 
-e MARIADB_ROOT_PASSWORD=:root_password 
-p :host_port:3306 
-v :volume:/var/lib/mysql 
mariadb::mariadb_version

Bind-mount alternative if you want the bytes at a specific host path:

bash
mkdir -p ~/docker-data/mariadb
docker run -d --name :container_name 
-e MARIADB_ROOT_PASSWORD=:root_password 
-p :host_port:3306 
-v ~/docker-data/mariadb:/var/lib/mysql 
mariadb::mariadb_version

Trade-offs are the same as MySQL: named volume is easier and avoids permission flips; bind mount is convenient for host-side backup tooling. Full breakdown in Docker Volumes vs Bind Mounts.

Practical usage: connecting

From the host (with mariadb or mysql CLI installed):

bash
mariadb -h 127.0.0.1 -P :host_port -u root -pormysql -h 127.0.0.1 -P :host_port -u root -p

Through the container's own CLI without installing one on the host:

bash
docker exec -it :container_name mariadb -u root -p

Connection string for an app on the host:

code
mysql://root:change-me@127.0.0.1::host_port/myapp

(Most drivers still call it mysql:// even when talking to MariaDB — the wire protocol is the same.)

Switching from MySQL to MariaDB (or back)

A common scenario: you have a MySQL container with a volume full of data, and you want to switch the image to MariaDB. You usually can't reuse the same volume — MySQL's mysql system schema and MariaDB's diverge enough that pointing one at the other's data directory ends in a corrupted server or refusal to start. The safe procedure is:

bash
# Dump from MySQL
docker exec mysql-container \
  mysqldump -u root -p"$ROOT_PW" --single-transaction --all-databases > dump.sql

# Stop the old container
docker stop mysql-container && docker rm mysql-container

# Start MariaDB with a FRESH volume
docker run -d --name mariadb \
  -e MARIADB_ROOT_PASSWORD="$ROOT_PW" \
  -p 3306:3306 \
  -v mariadb-data:/var/lib/mysql \
  mariadb:11.4

# Wait for MariaDB to be ready, then import
docker exec -i mariadb mariadb -u root -p"$ROOT_PW" < dump.sql

Same shape in the other direction (MariaDB → MySQL). Always dump-and-restore across the two products; don't share a data directory.

Backups

bash
Logical backup with mariadb-dump (mariadb-dump is the modern name; mysqldump still works)docker exec :container_name 
mariadb-dump -u root -p:root_password --single-transaction --all-databases \
backup-$(date +%Y%m%d).sql

Volume-level snapshot (requires stopping the container for consistency):

bash
docker stop :container_name
docker run --rm -v :volume:/source:ro -v "$(pwd):/backup" alpine 
tar czf /backup/mariadb-data-$(date +%Y%m%d).tar.gz -C /source .
docker start :container_name

Docker Compose version

yaml
services:
db:
image: mariadb::mariadb_version
environment:
MARIADB_ROOT_PASSWORD: :root_password
MARIADB_DATABASE: myapp
MARIADB_USER: appuser
MARIADB_PASSWORD: apppass
ports:
"::host_port:3306"
volumes:
:volume:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
volumes:
:volume:

The image ships its own healthcheck.sh for the readiness check.

Common pitfalls

  • Reusing a MySQL volume with MariaDB. Don't. Dump and restore.
  • Init env vars ignored on second run. Same gotcha as MySQL — MARIADB_DATABASE only fires on first init of an empty volume. To re-init, remove the volume.
  • Port 3306 conflict. Pick a different host port if you already have something on 3306.
  • Old MySQL clients connecting to MariaDB. Usually fine, but check if you hit auth-plugin issues — MariaDB defaults to mysql_native_password for compatibility, which makes it easier for legacy clients than MySQL 8.0+'s caching_sha2_password.
  • Slow first start. MariaDB initializes the data directory on first run; takes 10-30 seconds depending on the host. Check docker logs :container_name if it seems stuck.

What to do next

FAQ

Sources

Authoritative references this article was fact-checked against.

TagsDockerMariaDBMySQLDatabasePersistenceDevOps

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Software Systems Architect · Senior Software Engineer · Engineering Leadership

Software systems architect and senior software engineer with more than two decades designing, building, and running production software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Now a CTO, though what I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts