Skip to content
Guide · Run databases in Docker

MySQL & MariaDB in Docker: Setup with Docker Compose

The official mysql and mariadb images on Docker Hub give you a local MySQL-compatible server in one command, and a short compose.yaml adds a web admin tool such as Adminer or phpMyAdmin. This guide covers which tags to pin, the documented environment variables for each image, persistent volumes, loading SQL on first start, connecting from the host and other containers, and taking a dump out of the container.

Steps checked 8 October 2026 against the official documentation for each product. Versions covered: MySQL 8.4.11 LTS, 9.7.2 LTS and 26.7.0 Innovation (mysql image); MariaDB 12.3.3 LTS and 13.0.2 (mariadb image); Adminer 6.1.1; phpMyAdmin 5.2.3. Next review due April 2027. Installers, versions and download pages change; follow the official page if a step differs.
Short answer
  • Pin a release line: mysql:8.4 or mysql:9.7 (both LTS). The latest tag of the mysql image now points to the 26.7 Innovation release.
  • For MariaDB, the lts tag is 12.3 and latest is 13.0. MariaDB uses MARIADB_* variables and the mariadb and mariadb-dump client names.
  • Mount a volume at /var/lib/mysql for both images. Environment variables and /docker-entrypoint-initdb.d files only apply to an empty data directory.
  • In Docker Compose, a healthcheck plus depends_on: condition: service_healthy stops Adminer, phpMyAdmin or your app from connecting before initialisation finishes.
  • Back up with docker exec and mysqldump (MySQL) or mariadb-dump (MariaDB); the MariaDB image no longer ships the mysqldump name.
How we know: Research-based: tags, environment variables and commands were checked against the official mysql, mariadb, adminer and phpmyadmin image documentation, the MySQL 8.4 Reference Manual, the MySQL download page and MariaDB's container documentation on 8 October 2026. We have not run these commands for this guide; if your image version differs, follow the official page.

Docker MySQL: pick a tag and start the server

Docker MySQL setups start from the mysql Docker Official Image, maintained by the Docker community and the MySQL team. Oracle now runs two LTS lines and an Innovation line, and the image tags follow them. LTS releases only receive fixes within a series; Innovation releases add features and behaviour changes and are supported until the next Innovation release. For a development database that should behave like production, pin the same LTS line your servers use.

Start MySQL 8.4 with a named volume
docker run --name mysql -e MYSQL_ROOT_PASSWORD=<your-password> -p 3306:3306 -v mysqldata:/var/lib/mysql -d mysql:8.4
Open the mysql client inside the container (prompts for the password)
docker exec -it mysql mysql -uroot -p
mysql image tags on 8 October 2026
TagVersionTrack
mysql:8.4 (also 8)8.4.11LTS
mysql:9.7 (also 9, lts)9.7.2LTS
mysql:26.7 (also 26, innovation, latest)26.7.0Innovation

Oracle's own MySQL images

The MySQL Reference Manual documents a separate set of images maintained by the MySQL team at the Oracle Container Registry, pulled as container-registry.oracle.com/mysql/community-server:tag. The manual states that these images are built for Linux platforms only and that other platforms are not supported. Most local setups, including Docker Desktop on Windows 11 and macOS, use the Docker Hub mysql image shown above.

MariaDB in Docker

The mariadb image documents latest as the latest stable version and lts as the last long-term support release. On 8 October 2026 that is 13.0.2 for latest and 12.3.3 for lts, with 11.8, 11.4, 10.11 and 10.6 also tagged. One of MARIADB_ROOT_PASSWORD, MARIADB_ROOT_PASSWORD_HASH, MARIADB_RANDOM_ROOT_PASSWORD or MARIADB_ALLOW_EMPTY_ROOT_PASSWORD is required.

Two image defaults differ from a package install: the configuration sets host-cache-size=0 and skip-name-resolve, which disables authentication of user@hostname accounts. Use IP or % host patterns for container accounts, or re-enable name resolution as the image documentation describes. Setting MARIADB_AUTO_UPGRADE runs mariadb-upgrade when you move an existing volume to a newer image, and keeps a backup of the system tables unless you disable it.

Start MariaDB LTS with a user, a database and a named volume
docker run --detach --name mariadb -p 3306:3306 -v mariadbdata:/var/lib/mysql --env MARIADB_ROOT_PASSWORD=<your-password> --env MARIADB_DATABASE=mydb --env MARIADB_USER=app --env MARIADB_PASSWORD=<app-password> mariadb:lts
Equivalent environment variables
Purposemysql imagemariadb image
Root password (required, or an alternative)MYSQL_ROOT_PASSWORDMARIADB_ROOT_PASSWORD
Database created on first startMYSQL_DATABASEMARIADB_DATABASE
Extra user with full rights on that databaseMYSQL_USER, MYSQL_PASSWORDMARIADB_USER, MARIADB_PASSWORD
Random root password printed to the logMYSQL_RANDOM_ROOT_PASSWORDMARIADB_RANDOM_ROOT_PASSWORD
Empty root password (not recommended)MYSQL_ALLOW_EMPTY_PASSWORDMARIADB_ALLOW_EMPTY_ROOT_PASSWORD
Read a value from a file (Docker secrets)Append _FILEAppend _FILE
Command-line client in the imagemysqlmariadb

Docker Compose: MySQL with Adminer or phpMyAdmin

The file below runs MySQL 8.4 with both web tools; keep whichever you prefer. It uses the current Compose Specification without the obsolete top-level version: key. The MySQL healthcheck uses mysqladmin ping, which the MySQL manual documents as returning 0 when the server is running, even if the connection is refused with Access denied, so no password is needed in the check.

compose.yaml: MySQL 8.4 with Adminer and phpMyAdmin
services:
  db:
    image: mysql:8.4
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?set MYSQL_ROOT_PASSWORD in .env}
      MYSQL_DATABASE: mydb
      MYSQL_USER: app
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:?set MYSQL_PASSWORD in .env}
    ports:
      - "127.0.0.1:3306:3306"
    volumes:
      - mysqldata:/var/lib/mysql
      - ./initdb:/docker-entrypoint-initdb.d:ro
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  adminer:
    image: adminer:6
    restart: unless-stopped
    environment:
      ADMINER_DEFAULT_SERVER: db
    ports:
      - "8080:8080"
    depends_on:
      db:
        condition: service_healthy

  phpmyadmin:
    image: phpmyadmin:5.2
    restart: unless-stopped
    environment:
      PMA_HOST: db
    ports:
      - "8081:80"
    depends_on:
      db:
        condition: service_healthy

volumes:
  mysqldata:
.env in the same folder
MYSQL_ROOT_PASSWORD=<your-root-password>
MYSQL_PASSWORD=<your-app-password>
MariaDB variant of the db service, with the image's healthcheck.sh
  db:
    image: mariadb:lts
    restart: unless-stopped
    environment:
      MARIADB_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?set MYSQL_ROOT_PASSWORD in .env}
      MARIADB_DATABASE: mydb
      MARIADB_USER: app
      MARIADB_PASSWORD: ${MYSQL_PASSWORD:?set MYSQL_PASSWORD in .env}
    volumes:
      - mariadbdata:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      start_period: 10s
      interval: 10s
      timeout: 5s
      retries: 3

Start it and sign in

  1. Run docker compose up -d in the folder, then docker compose ps until db shows healthy.
  2. Adminer: open http://localhost:8080. ADMINER_DEFAULT_SERVER sets the default server to db; sign in as app or root.
  3. phpMyAdmin: open http://localhost:8081. PMA_HOST points it at db, and it signs in with your MySQL credentials.

The Adminer image supports MySQL, PostgreSQL, SQLite, SimpleDB and Elasticsearch out of the box, so the same container can also manage the Postgres stack from the PostgreSQL in Docker guide. phpMyAdmin is for MySQL and MariaDB only. If you use the MariaDB variant, rename the volume in the top-level volumes: section to mariadbdata.

Loading init SQL and existing dumps

On the first start, both images run files found in /docker-entrypoint-initdb.d in alphabetical order: .sh, .sql, .sql.gz, .sql.xz and .sql.zst for both, plus .sql.bz2 for the mysql image. SQL files are imported into the database named by MYSQL_DATABASE (or MARIADB_DATABASE). That makes this folder the simplest way to seed a container from a dump: put 01-schema.sql and 02-data.sql.gz in ./initdb and start from an empty volume.

Nothing in the folder runs again once the volume contains a database. With an initialised volume the images also ignore the password and database variables; for the mysql image, MYSQL_ROOT_PASSWORD should be omitted in that case because it will be ignored anyway. To load a dump into an existing container, pipe it to the client instead, as shown in the backup section.

initdb/01-schema.sql (MySQL / MariaDB)
CREATE TABLE customers (
    customer_id INT AUTO_INCREMENT PRIMARY KEY,
    name        VARCHAR(100) NOT NULL,
    created_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO customers (name) VALUES ('Ada'), ('Grace');

Docker MySQL host: connecting from your machine and other containers

From the host. With -p 3306:3306, tools such as MySQL Workbench, DBeaver or the mysql client connect to host 127.0.0.1 on port 3306. The Compose file publishes the port as 127.0.0.1:3306:3306, which Docker documents as keeping the port local to the machine. If a local MySQL or MariaDB service already uses 3306, map another host port, such as 3307:3306.

From other containers. In Compose, every service joins a default network and is reachable by its service name, so the host name for the database is db, not localhost. With plain docker run, create a user-defined network and start both containers on it, then use the container name as the host. The mysql image documentation warns that setting a MYSQL_HOST variable is known to cause issues with the image, so set your application's own host variable instead.

Server options can be passed after the image name, for example mysql:8.4 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci, or placed in a .cnf file mounted at /etc/mysql/conf.d.

Connection errors and their usual cause in Docker
ErrorUsual causeWhat to check
ERROR 2003: Can't connect to MySQL serverPort not published, wrong host port, or the server is still initialisingdocker compose ps, the ports mapping, and the docker logs output
ERROR 1045 (28000): Access denied for userWrong password, or the volume was initialised earlier with different credentialsCredentials in the existing volume win over changed environment variables
App container cannot reach localhost:3306Inside a container, localhost is the container itselfUse the service name (db) as the host
Connection refused right after docker compose upNo connections are accepted until initialisation completesAdd a healthcheck and condition: service_healthy

Backing up and restoring a MySQL or MariaDB container

The image documentation recommends running the dump tool inside the container with docker exec, so it can reach the server without extra networking. Note the single quotes: the password variable is expanded inside the container, not on your host.

MySQL: dump all databases to a file on the host (bash)
docker exec mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > all-databases.sql
MySQL: restore a dump file into the container (bash)
docker exec -i mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < all-databases.sql
MariaDB: logical backup written into a /backup volume
docker exec mariadb sh -c 'mariadb-dump --all-databases -u root -p"$MARIADB_ROOT_PASSWORD" > backup/db.sql'

Notes for Windows and MariaDB

In PowerShell, < cannot be used for input redirection, so run the restore line from cmd or Git Bash, or copy the file into the container with docker cp first. The MariaDB example assumes a volume mounted at /backup as in MariaDB's container backup documentation. From MariaDB 11.0 the mysqldump name is deprecated and removed from the mariadb image, so scripts must call mariadb-dump. For physical backups, the image also includes mariadb-backup.

Options for consistent dumps (--single-transaction, --routines, --events), restoring to another server and scheduling are covered in the mysqldump guide listed under related pages.

Frequently asked questions

Which docker hub mysql tag should I use?

Pin the LTS line that matches your servers: mysql:8.4 or mysql:9.7. Avoid latest for anything you keep, because on 8 October 2026 it points to the 26.7 Innovation release, and Innovation releases can change behaviour between versions.

Is Adminer in Docker safe to expose?

Treat Adminer and phpMyAdmin as local development tools. Both give full database access to anyone who can reach the port and sign in, so publish them on 127.0.0.1 or keep them off servers reachable from the internet.

What is the difference between the mysql and mariadb images?

They run different servers. The mysql image runs Oracle's MySQL and uses MYSQL_* variables; the mariadb image runs MariaDB Server, uses MARIADB_* variables and names its tools mariadb, mariadb-dump and mariadb-backup. Both store data in /var/lib/mysql. See MariaDB vs MySQL for how the servers differ.

Why is my docker mysql database empty after restarting?

If no volume or bind mount is attached at /var/lib/mysql, the data lives in an anonymous volume tied to that container; re-creating the container starts a new, empty one. Use a named volume as in the examples above and avoid docker compose down -v, which deletes named volumes.

How do I change the MySQL root password in Docker?

Changing MYSQL_ROOT_PASSWORD has no effect once the volume holds a database. Connect with the current password and change it with SQL; MariaDB documents a separate procedure for resetting lost passwords in its Docker Official Image FAQ.

Sources

Checked 8 October 2026.

How we research guides: our editorial method. We link only to official downloads and never host installers.

Database installed?

Write your first queries with the free beginner course, then practise on real problems.