DEV Community

Cover image for MariaDB Docker setup — Running MariaDB in Docker containers complete guide
Piter Adyson
Piter Adyson

Posted on

MariaDB Docker setup — Running MariaDB in Docker containers complete guide

Running MariaDB in Docker simplifies deployment, makes environments reproducible, and allows you to spin up databases in seconds. Whether you need a quick dev environment or a production-ready setup, Docker handles the complexity of installation and configuration. This guide covers everything from basic container setup to production configurations with persistence, custom settings and proper backup strategies.

MariaDB in Docker

Why run MariaDB in Docker
Traditional MariaDB installation means dealing with package managers, version conflicts, and cleanup when things go wrong. Docker containers provide isolation and consistency that bare-metal installations can't match.

Benefits of containerized databases
Docker containers package MariaDB with all its dependencies. You get the same behavior on your laptop, CI server, and production environment. No more "works on my machine" problems.

Containers start in seconds. Spinning up a fresh MariaDB instance takes about 5-10 seconds compared to minutes for traditional installation. This matters when running integration tests or switching between projects.

Cleanup is trivial. Delete the container and it's gone. No leftover configuration files, no orphaned data directories, no package conflicts with other software.

When Docker makes sense
Docker works well for development environments where you need quick setup and teardown. It's also suitable for microservices architectures where each service gets its own database. Testing and CI pipelines benefit from reproducible database instances.

For production, Docker adds complexity but provides consistency across environments. You trade some raw performance for operational benefits. The overhead is typically 1-3% for database workloads, which is acceptable for most applications.

Quick start with Docker run
The fastest way to get MariaDB running is a single Docker command. This works for testing and development.

Basic container setup
Start MariaDB with minimal configuration:

docker run -d \
--name mariadb \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
mariadb:11
This starts MariaDB 11 in the background with root password set. The container runs until you stop it.

Check if it's running:

docker ps
Connect to the database:

docker exec -it mariadb mariadb -u root -p
Environment variables for initial setup
MariaDB's Docker image supports several environment variables for first-run configuration:

Variable Description Required
MYSQL_ROOT_PASSWORD Root user password Yes (or use one of the alternatives)
MYSQL_ALLOW_EMPTY_PASSWORD Allow empty root password No
MYSQL_RANDOM_ROOT_PASSWORD Generate random root password No
MYSQL_DATABASE Create database on startup No
MYSQL_USER Create non-root user No
MYSQL_PASSWORD Password for non-root user No
Create a database and user on startup:

docker run -d \
--name mariadb \
-e MYSQL_ROOT_PASSWORD=rootpassword \
-e MYSQL_DATABASE=myapp \
-e MYSQL_USER=appuser \
-e MYSQL_PASSWORD=apppassword \
mariadb:11
Exposing ports
By default, MariaDB runs on port 3306 inside the container. Map it to your host:

docker run -d \
--name mariadb \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
mariadb:11
Now you can connect from your host machine:

mariadb -h 127.0.0.1 -u root -p
Use a different host port if 3306 is already in use:

docker run -d \
--name mariadb \
-p 3307:3306 \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
mariadb:11
Data persistence with volumes
Without volumes, your data disappears when the container stops. That's fine for throwaway test databases, but production needs persistence.

Named volumes
Docker named volumes are the simplest approach:

docker run -d \
--name mariadb \
-v mariadb-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
mariadb:11
The volume mariadb-data persists even after container deletion. List your volumes:

docker volume ls
Inspect volume details:

docker volume inspect mariadb-data
Bind mounts
Bind mounts map a host directory into the container. Useful when you need direct access to data files:

docker run -d \
--name mariadb \
-v /path/to/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
mariadb:11
Make sure the directory exists and has proper permissions. On Linux, the MySQL user inside the container needs write access:

mkdir -p /path/to/data
chown -R 999:999 /path/to/data
The UID 999 is the MySQL user inside the MariaDB container.

Volume backup
Back up a named volume by running a temporary container:

docker run --rm \
-v mariadb-data:/source:ro \
-v $(pwd):/backup \
alpine tar czf /backup/mariadb-backup.tar.gz -C /source .
This creates a compressed archive of the entire data directory. For proper database backups, use mysqldump or mariabackup instead, which we'll cover later.

Docker Compose for MariaDB
Docker Compose makes multi-container setups manageable and configurations version-controlled.

Basic compose file
Create a docker-compose.yml:

services:
mariadb:
image: mariadb:11
container_name: mariadb
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
MYSQL_PASSWORD: apppassword
ports:
- "3306:3306"
volumes:
- mariadb-data:/var/lib/mysql
restart: unless-stopped

volumes:
mariadb-data:
Start the service:

docker compose up -d
Stop and remove:

docker compose down
Remove including volumes:

docker compose down -v
Application with MariaDB
A typical setup includes your application and MariaDB together:

services:
app:
build: .
environment:
DATABASE_URL: mysql://appuser:apppassword@mariadb:3306/myapp
depends_on:
mariadb:
condition: service_healthy
ports:
- "8080:8080"

mariadb:
image: mariadb:11
container_name: mariadb
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
MYSQL_PASSWORD: apppassword
volumes:
- mariadb-data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped

volumes:
mariadb-data:
The depends_on with condition: service_healthy ensures your app waits for MariaDB to be ready before starting.

Custom configuration
Default settings work for development but production often needs tuning.

Configuration file mount
Create a custom configuration file my.cnf:

[mysqld]

InnoDB settings

innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT

Connection settings

max_connections = 200
wait_timeout = 600

Query cache (disabled in MariaDB 10.1.7+)

query_cache_type = 0

Logging

slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2

Character set

character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
Mount it into the container:

docker run -d \
--name mariadb \
-v ./my.cnf:/etc/mysql/conf.d/custom.cnf:ro \
-v mariadb-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
mariadb:11
Files in /etc/mysql/conf.d/ are read automatically.

Docker Compose with custom config
services:
mariadb:
image: mariadb:11
container_name: mariadb
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
volumes:
- mariadb-data:/var/lib/mysql
- ./my.cnf:/etc/mysql/conf.d/custom.cnf:ro
ports:
- "3306:3306"
restart: unless-stopped

volumes:
mariadb-data:
Common configuration options
Key settings to consider for production:

Setting Default Production recommendation
innodb_buffer_pool_size 128M 50-70% of available RAM
innodb_log_file_size 48M 256M-1G depending on write load
max_connections 151 Based on expected concurrent connections
innodb_flush_log_at_trx_commit 1 1 for durability, 2 for performance
Verify your configuration is applied:

docker exec mariadb mariadb -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
Initialization scripts
The MariaDB Docker image can run SQL scripts on first startup. This is useful for schema creation and seed data.

SQL initialization
Place .sql, .sql.gz, or .sql.xz files in /docker-entrypoint-initdb.d/:

Create init/01-schema.sql:

CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
Create init/02-seed.sql:

INSERT INTO users (email) VALUES ('admin@example.com');
INSERT INTO users (email) VALUES ('user@example.com');
Mount the init directory:

services:
mariadb:
image: mariadb:11
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
MYSQL_DATABASE: myapp
volumes:
- mariadb-data:/var/lib/mysql
- ./init:/docker-entrypoint-initdb.d:ro
restart: unless-stopped

volumes:
mariadb-data:
Scripts run in alphabetical order, only on first container start (when data directory is empty).

Shell script initialization
For more complex setup, use shell scripts:

Create init/00-setup.sh:

!/bin/bash

set -e

mariadb -u root -p"$MYSQL_ROOT_PASSWORD" <<-EOSQL
CREATE USER IF NOT EXISTS 'readonly'@'%' IDENTIFIED BY 'readonlypassword';
GRANT SELECT ON myapp.* TO 'readonly'@'%';
FLUSH PRIVILEGES;
EOSQL
Make it executable:

chmod +x init/00-setup.sh
Networking
Docker networking controls how containers communicate with each other and the outside world.

Default bridge network
Containers on the default bridge network can communicate via IP address but not hostname. For development this usually works fine:

docker run -d --name mariadb -e MYSQL_ROOT_PASSWORD=pw mariadb:11
docker run -it --rm mariadb:11 mariadb -h $(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mariadb) -u root -p
Custom networks
Custom networks allow hostname-based communication:

docker network create myapp-network

docker run -d \
--name mariadb \
--network myapp-network \
-e MYSQL_ROOT_PASSWORD=pw \
mariadb:11

docker run -it --rm \
--network myapp-network \
mariadb:11 \
mariadb -h mariadb -u root -p
The second container can reach MariaDB using hostname mariadb.

Compose networking
Docker Compose creates a network automatically. Services communicate by service name:

services:
app:
image: myapp
environment:
DB_HOST: mariadb # Use service name as hostname
DB_PORT: 3306

mariadb:
image: mariadb:11
environment:
MYSQL_ROOT_PASSWORD: pw
Health checks and monitoring
Proper health checks ensure containers are actually ready to serve traffic, not just running.

Built-in health check
MariaDB's Docker image includes a health check script:

services:
mariadb:
image: mariadb:11
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
Check health status:

docker inspect --format='{{.State.Health.Status}}' mariadb
Custom health check
For specific requirements, write custom checks:

healthcheck:
test:
[
"CMD",
"mariadb",
"-u",
"root",
"-p$$MYSQL_ROOT_PASSWORD",
"-e",
"SELECT 1",
]
interval: 10s
timeout: 5s
retries: 5
Monitoring with logs
View container logs:

docker logs mariadb
Follow logs in real-time:

docker logs -f mariadb
Limit output to recent entries:

docker logs --tail 100 mariadb
Enable slow query logging in your configuration to catch performance issues:

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
Mount a volume for logs:

volumes:

  • mariadb-data:/var/lib/mysql
  • mariadb-logs:/var/log/mysql Backup strategies for Docker MariaDB Data in containers needs the same backup discipline as traditional installations. Docker adds some nuances but the fundamentals remain.

Using mysqldump in Docker
Run mysqldump inside the container:

docker exec mariadb mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" myapp > backup.sql
For all databases:

docker exec mariadb mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --all-databases > all_databases.sql
Compressed backup:

docker exec mariadb mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" myapp | gzip > backup.sql.gz
Scheduled backups with cron
Create a backup script:

!/bin/bash

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups"
CONTAINER="mariadb"

docker exec $CONTAINER mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --all-databases | \
gzip > "$BACKUP_DIR/mariadb_${TIMESTAMP}.sql.gz"

Keep only last 7 days

find $BACKUP_DIR -name "mariadb_*.sql.gz" -mtime +7 -delete
Add to crontab for daily 3 AM backups:

0 3 * * * /usr/local/bin/mariadb-backup.sh
Using Databasus for automated backups
Manual backup scripts work but require maintenance and lack monitoring. Databasus (an industry standard for MariaDB backup) provides automated backups with a web interface, scheduling, and notifications.

Install Databasus on a separate server using Docker:

docker run -d \
--name databasus \
-p 4005:4005 \
-v ./databasus-data:/databasus-data \
--restart unless-stopped \
databasus/databasus:latest
Or with Docker Compose:

services:
databasus:
image: databasus/databasus:latest
container_name: databasus
ports:
- "4005:4005"
volumes:
- databasus-data:/databasus-data
restart: unless-stopped

volumes:
databasus-data:
Access the web interface at http://localhost:4005, then:

Add your database — Click "New Database", select MariaDB, enter your MariaDB server's connection details (host, port, credentials)
Select storage — Choose local storage, S3, Google Cloud Storage, or other supported destinations
Select schedule — Set backup frequency: hourly, daily, weekly, or custom cron expression
Click "Create backup" — Databasus handles backup execution, compression, retention and notifications
Databasus supports multiple notification channels including Slack, Discord, Telegram and email, so you know immediately when backups succeed or fail.

Security considerations
Running databases in containers doesn't change security requirements. If anything, you need to be more careful about configuration.

Secure root password
Never use default or weak passwords. Use environment variables from secrets management:

services:
mariadb:
image: mariadb:11
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mariadb_root_password
secrets:
- mariadb_root_password

secrets:
mariadb_root_password:
file: ./secrets/mariadb_root_password.txt
For Docker Swarm, use proper Docker secrets:

echo "my-secret-pw" | docker secret create mariadb_root_password -
Network isolation
Don't expose database ports to the public internet. Use internal Docker networks:

services:
app:
networks:
- frontend
- backend

mariadb:
networks:
- backend # Only accessible from backend network

networks:
frontend:
backend:
internal: true # No external access
Read-only root filesystem
For extra security, run with read-only root filesystem:

services:
mariadb:
image: mariadb:11
read_only: true
tmpfs:
- /tmp
- /run/mysqld
volumes:
- mariadb-data:/var/lib/mysql
Resource limits
Prevent runaway containers from consuming all resources:

services:
mariadb:
image: mariadb:11
deploy:
resources:
limits:
cpus: "2"
memory: 4G
reservations:
cpus: "1"
memory: 2G
Production checklist
Before running MariaDB Docker containers in production, verify these items:

Data persistence configured with volumes
Custom configuration tuned for workload
Health checks enabled
Automated backup strategy in place
Secrets managed securely (not hardcoded)
Network properly isolated
Resource limits set
Monitoring and alerting configured
Restart policy set to unless-stopped or always
Container image version pinned (not using latest)
Troubleshooting common issues
Container exits immediately
Check logs for errors:

docker logs mariadb
Common causes: missing MYSQL_ROOT_PASSWORD, corrupt data directory, or permission issues on mounted volumes.

Permission denied on bind mount
Ensure the host directory has correct ownership:

sudo chown -R 999:999 /path/to/data
Or run MariaDB with your user ID:

docker run -d --user $(id -u):$(id -g) ...
Can't connect from host
Verify port mapping:

docker port mariadb
Check if MariaDB is listening on all interfaces. By default it should, but verify with:

docker exec mariadb mariadb -u root -p -e "SHOW VARIABLES LIKE 'bind_address';"
Slow performance
Check if InnoDB buffer pool is sized correctly:

docker exec mariadb mariadb -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
For Docker Desktop on macOS/Windows, file system performance through volumes can be slow. Use named volumes instead of bind mounts for better performance.

Conclusion
Running MariaDB in Docker provides consistent environments across development, testing and production. Start with simple docker run commands for quick setups, then move to Docker Compose for more complex configurations. Always configure data persistence with volumes, set up proper health checks, and implement automated backups. The overhead of containerization is minimal compared to the operational benefits of reproducibility and isolation. Pin your image versions, tune your configuration for your workload, and treat container security with the same rigor as traditional deployments.

Top comments (0)