How to Deploy MySQL/MariaDB in Docker with Persistent Data Storage | INTROSERV
EUR
european

EUR

usa

USD

English En
Ex. VAT Ex. VAT 0%

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

Tip

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 sudo privileges
  • User added to the docker group (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.

Info

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

Info

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

Info

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:

Info

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

VAT

  • Other

    Ex. VAT

    0%
  • austria

    Austria

    20%
  • Belgium

    Belgium

    21%
  • Bulgaria

    Bulgaria

    20%
  • Croatia

    Croatia

    25%
  • Cyprus

    Cyprus

    19%
  • Czech Republic

    Czech Republic

    21%
  • Denmark

    Denmark

    25%
  • Estonia

    Estonia

    22%
  • France

    France

    20%
  • Finland

    Finland

    24%
  • Germany

    Germany

    19%
  • Greece

    Greece

    24%
  • Hungary

    Hungary

    27%
  • Ireland

    Ireland

    23%
  • Italy

    Italy

    22%
  • Latvia

    Latvia

    21%
  • Lithuania

    Lithuania

    21%
  • Luxembourg

    Luxembourg

    17%
  • Malta

    Malta

    18%
  • Netherlands

    Netherlands

    21%
  • Poland

    Poland

    23%
  • Portugal

    Portugal

    23%
  • Romania

    Romania

    19%
  • Slovakia

    Slovakia

    20%
  • Slovenia

    Slovenia

    22%
  • Spain

    Spain

    21%
  • Sweden

    Sweden

    25%
  • USA

    USA

    0%
european
states
  • germany
  • Español
  • Italiano
  • Poland
  • Русский
  • Slovenski
  • Türkçe
  • ukraine
  • kingdom
  • French
  • Hrvatska
  • Other
  • Austria
  • Belgium
  • Bulgaria
  • Croatia
  • Cyprus
  • Czech Republic
  • Denmark
  • Estonia
  • Finland
  • France
  • Germany
  • Greece
  • Hungary
  • Ireland
  • Italy
  • Latvia
  • Lithuania
  • Luxembourg
  • Malta
  • Netherlands
  • Poland
  • Portugal
  • Romania
  • Slovakia
  • Slovenia
  • Spain
  • Sweden
  • USA