How to Deploy MySQL/MariaDB in Docker with Persistent Data Storage
Introduction
In this tutorial, you deploy a MySQL or MariaDB database container using Docker with persistent data storage. Running databases in containers simplifies deployment, version control, and portability across environments. By the end of this tutorial, you will have a production-ready MySQL or MariaDB instance running in Docker with data stored safely outside the container.
Prerequisites
Target audience: Intermediate system administrators
Estimated time to complete: ~30–45 minutes
Operating System
- Ubuntu 24.04 LTS (tested)
- Also compatible with: Ubuntu 22.04 LTS, Debian 12 / 13
Software Requirements
- Docker 24.0.6 or later
- Docker Compose v2 (included with modern Docker installations)
Check your Docker version:
docker --version
Expected output: Docker version 24.0.6, build ed223bc
Check Docker Compose version:
docker compose version
Expected output: Docker Compose version v5.1.0
If you don't have Docker installed, you will install it during Step 1.
Hardware Requirements
- Minimum 2 GB RAM
- At least 10 GB free disk space
Network Requirements
- Port 3306 available (default MySQL/MariaDB port)
- Firewall configured to allow access only if external connections are required
Permissions
- A non-root user with
sudoprivileges - User added to the
dockergroup (recommended)
Assumed Knowledge
- Basic Docker concepts (images, containers, volumes)
- Comfortable with command line operations
Step 1: Install and Verify Docker
If Docker is not installed, install it:
sudo apt update && sudo apt upgrade -y && sudo apt install curl
curl -sSL https://get.docker.com/ | CHANNEL=stable bash
sudo apt install docker-ce docker-compose-plugin -y
Enable and start Docker:
sudo systemctl enable docker
sudo systemctl start docker
Verify Docker is running:
sudo systemctl status docker
Expected result: Docker service status shows active (running).
Add your user to the docker group to avoid using sudo while using Docker:
sudo usermod -aG docker <YOUR_USERNAME>
newgrp docker
Step 2: Create a Persistent Data Directory
A bind mount maps a directory from the host system directly into a container, making it accessible inside the container's filesystem.
Create a directory for database data and set proper permissions:
mkdir -p ~/mysql-docker/data
chmod 750 ~/mysql-docker/data
This directory will store database files outside the container.
Without persistent storage, all database data is lost when the container is removed.
Step 3: Create a Docker Compose Configuration
Navigate to your project directory:
cd ~/mysql-docker
Create a docker-compose.yml file:
nano docker-compose.yml
Add the following configuration (MySQL example):
services: mysql: image: mysql:8.0.36 container_name: mysql-server restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: <YOUR_STRONG_ROOT_PASSWORD> MYSQL_DATABASE: appdb MYSQL_USER: appuser MYSQL_PASSWORD: <YOUR_STRONG_USER_PASSWORD> ports: - "3306:3306" volumes: - ./data:/var/lib/mysql
MariaDB alternative — if you prefer MariaDB, use:
services: mariadb: image: mariadb:11.3 container_name: mariadb-server restart: unless-stopped environment: MARIADB_ROOT_PASSWORD: <YOUR_STRONG_ROOT_PASSWORD> MARIADB_DATABASE: appdb MARIADB_USER: appuser MARIADB_PASSWORD: <YOUR_STRONG_USER_PASSWORD> ports: - "3306:3306" volumes: - ./data:/var/lib/mysql
Important: Never hardcode real passwords in version control. Use environment variables or a .env file in production. Replace <YOUR_STRONG_USER_PASSWORD> and <YOUR_STRONG_ROOT_PASSWORD> with your secure passwords.
Step 4: Start the Database Container
Run:
docker compose up -d
Expected output:
- Docker pulls the image (if not already downloaded)
- Container starts in detached mode
Verify container status:
docker ps
Expected output:
CONTAINER ID IMAGE STATUS PORTS abc123def456 mysql:8.0.36 Up 10 seconds 0.0.0.0:3306->3306/tcp
Step 5: Verify Database Functionality
Connect to the Container
For MySQL container execute:
docker exec -it mysql-server mysql -u root -p
For MariaDB container execute:
docker exec -it mariadb-server mysql -u root -p
Enter your root password.
Expected result: MySQL shell prompt appears: mysql>
Check Databases
SHOW DATABASES;
You should see:
- appdb
- information_schema
- mysql
- performance_schema
- sys
EXIT;
Test Data Persistence
Create a test table
docker exec -it mysql-server mysql -u root -p
Inside MySQL shell enter the following commands:
USE appdb;
CREATE TABLE test_table (id INT PRIMARY KEY);
EXIT;
Stop and remove the container
docker compose down
Restart the container
docker compose up -d
Verify the table still exists
docker exec -it mysql-server mysql -u root -p
Inside MySQL shell enter the following commands:
USE appdb;
SHOW TABLES;
Expected result: test_table appears. This confirms persistent storage is working.
Step 6: Secure the Deployment
Restrict External Access
If database access is only needed internally, modify ports in docker-compose.yml:
ports: - "127.0.0.1:3306:3306"
Restart:
docker compose down
docker compose up -d
Important: Avoid exposing port 3306 publicly unless absolutely necessary.
Use a .env File
Create .env:
nano .env
Example:
MYSQL_ROOT_PASSWORD=<YOUR_STRONG_ROOT_PASSWORD> MYSQL_PASSWORD=<YOUR_STRONG_USER_PASSWORD>
Reference in docker-compose.yml:
environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_PASSWORD: ${MYSQL_PASSWORD}
Verification
You can confirm everything works correctly by:
- Running
docker ps— container is Up - Connecting via MySQL CLI — login succeeds
- Restarting container — data persists
- Checking logs:
docker logs mysql-server
Expected log entries: "ready for connections".
Reverting Changes
To stop and remove the container:
docker compose down
To remove persistent data:
Important: This permanently deletes all database data.
rm -rf ~/mysql-docker/data
To uninstall Docker:
sudo apt remove docker-ce docker-compose-plugin -y
Troubleshooting
Container Fails to Start
Check logs:
docker logs mysql-server
Common causes:
- Weak password rejected
- Port 3306 already in use
Check port usage:
sudo ss -tulnp | grep 3306
Permission Errors
If you see permission denied errors:
sudo chown -R 999:999 ~/mysql-docker/data
999 is commonly the MySQL container user ID.
Conclusion & Next Steps
You successfully deployed MySQL or MariaDB in Docker with persistent storage. The database now runs in an isolated container while safely storing data on the host system.
Next steps:
- Configure automated backups
- Implement replication for high availability
- Integrate with application containers
- Add monitoring with Prometheus and Grafana