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 KarunaratneIshan Karunaratne⏱️ 6 min readUpdated
Share thisCopied

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 container
docker stop :container_name && docker rm :container_name

# Start a new MariaDB against the same volume — your data is still there
docker 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 -p
# or
mysql -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

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts