XAMPP MySQL Shutdown Unexpectedly? My Permanent Fix Using Docker
XAMPP MySQL Shutdown Unexpectedly? My Permanent Fix Using Docker
You open XAMPP, click Start beside Apache, then start MySQL.
For a second, everything looks fine.
Then MySQL turns red.
Error: MySQL shutdown unexpectedly.
You try again.
Same result.
You restart XAMPP. Run it as administrator. Restart Windows. Change a port. Copy some files. Search Stack Overflow. Maybe it works again for a few days.
Then one morning, the same error comes back.
I got tired of dealing with exactly this problem.
Instead of repeatedly trying to repair XAMPP's MySQL service, I changed my local development setup completely:
I still use XAMPP for Apache and PHP, but I run MySQL separately inside Docker.
And that small change made my local PHP development setup much less frustrating.
My setup now looks like this:
XAMPP
├── Apache
└── PHP
Docker
├── MySQL
├── phpMyAdmin
└── Persistent MySQL Volume
That's it.
No complicated Docker setup.
No need to move the PHP application into Docker.
No need to create a Dockerfile for PHP.
You can continue putting your PHP projects inside:
C:\xampp\htdocs\
and opening them normally through:
http://localhost/my-project
The only difference is that your PHP application connects to MySQL running inside Docker instead of the database server bundled with XAMPP.
In this guide, I'll show you exactly how.
Why Does XAMPP MySQL Keep Shutting Down?
There isn't one single reason behind every "MySQL shutdown unexpectedly" error.
It can happen because of things such as:
- Another process using the database port
- Improper shutdowns
- Corrupted database files
- Database configuration problems
- Windows stopping the service incorrectly
- Conflicts with another MySQL/MariaDB installation
- Problems after a system crash
There is also something beginners often don't realize.
Although the XAMPP Control Panel displays the service as MySQL, modern XAMPP versions actually ship MariaDB instead of MySQL. Apache Friends says XAMPP switched from MySQL to MariaDB starting with XAMPP 5.5.30 and 5.6.14.
That isn't normally a problem because MySQL and MariaDB are highly compatible for common PHP development.
The problem for me wasn't whether the service was technically MySQL or MariaDB.
The problem was simpler:
I didn't want my database disappearing every time XAMPP decided it didn't want to start.
My Solution: Stop Running MySQL Through XAMPP
Instead of fixing the same XAMPP database problem again and again, I stopped using XAMPP for the database entirely.
I now use:
- XAMPP Apache for the web server
- XAMPP PHP for PHP
- Docker MySQL for the database
- Docker phpMyAdmin for database management
- Docker Volume for persistent database storage
Apache Friends officially notes that you can use your own MySQL server with XAMPP; you simply shouldn't start XAMPP's bundled database server at the same time on the same port.
That makes this hybrid setup surprisingly simple.
[Suggested Diagram: XAMPP Apache/PHP connecting from Windows to MySQL running inside Docker, with phpMyAdmin connecting to the same MySQL container.]
Why I Prefer This Setup
The biggest reason is simple:
My database is no longer dependent on XAMPP's MySQL/MariaDB service.
When I open XAMPP, I only care about Apache.
My database has its own container and persistent Docker volume.
There are a few other benefits too.
1. Your Database Has Persistent Storage
Docker containers themselves shouldn't be treated as permanent storage.
That's why we attach a Docker volume to:
/var/lib/mysql
The MySQL files remain in the volume even when the MySQL container stops or is recreated.
Docker's current database documentation recommends this same pattern when running MySQL through Compose.
Think of the container as a rented office and the Docker volume as your filing cabinet.
You can close the office, reopen it, or replace it.
Your files remain in the cabinet.
2. You Can Still Use XAMPP Normally
This is probably my favorite part.
You don't have to Dockerize your entire PHP development environment.
Keep your projects inside:
C:\xampp\htdocs
Keep running Apache using XAMPP.
Keep opening:
http://localhost/project-name
Only your database moves to Docker.
For someone beginning with Docker, this is much easier than putting Apache, PHP, MySQL, Composer, and everything else into containers on day one.
3. You Still Get phpMyAdmin
Moving MySQL into Docker doesn't mean giving up phpMyAdmin.
We'll run phpMyAdmin in another container.
Both containers automatically share a Docker Compose network, allowing phpMyAdmin to reach MySQL using the database service name.
The official phpMyAdmin Docker image supports configuring the database server through PMA_HOST and PMA_PORT.
You'll simply open:
http://localhost:8080
and use phpMyAdmin almost exactly as you did before.
What You Need
Before continuing, install:
- XAMPP
- Docker Desktop
- A text editor such as VS Code
You should also make sure Docker Desktop is running.
That's pretty much everything.
Step 1: Stop MySQL in XAMPP
Open the XAMPP Control Panel.
Start:
Apache
But do not start MySQL.
Your control panel should effectively look like:
Apache Running
MySQL Stopped
From now on, XAMPP handles PHP and Apache while Docker handles MySQL.
Important: You generally don't want XAMPP's database server and Docker's MySQL trying to bind to the same Windows port.
Apache Friends specifically warns that two database servers can't start on the same port.
Step 2: Create a Folder for Your Docker Database
Create a folder anywhere you like.
For example:
C:\docker\mysql
Inside it, create:
docker-compose.yml
That's the main file we need.
You don't need a PHP Dockerfile.
You don't need an Apache Dockerfile.
You don't need to rebuild your PHP project.
Step 3: Add This Docker Compose File
Paste the following configuration into docker-compose.yml:
services:
mysql:
image: mysql:8.4
container_name: xampp-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: my_database
MYSQL_USER: my_user
MYSQL_PASSWORD: my_password
ports:
- "3307:3306"
volumes:
- mysql_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin:latest
container_name: xampp-phpmyadmin
restart: unless-stopped
depends_on:
- mysql
environment:
PMA_HOST: mysql
PMA_PORT: 3306
ports:
- "8080:80"
volumes:
mysql_data:
That's basically the entire setup.
The official MySQL Docker image currently provides the 8.4 tag, while Docker's database documentation demonstrates the same architecture of exposing MySQL on host port 3307, storing /var/lib/mysql in a named volume, and connecting phpMyAdmin to the database container.
Understanding the Docker Compose File
Don't worry if Docker Compose looks strange at first.
Let's break it down.
MySQL Service
mysql:
image: mysql:8.4
This tells Docker:
Start a container using MySQL 8.4.
MySQL Root Password
MYSQL_ROOT_PASSWORD: root_password
This creates the MySQL root password during the initial database setup.
For local development, replace it with your preferred password.
Warning: Don't use simple credentials like these on a public or production server. This tutorial is intended for local development.
Automatically Create a Database
MYSQL_DATABASE: my_database
MySQL creates this database during the first initialization of an empty data directory.
You could rename it to something related to your project:
MYSQL_DATABASE: ecommerce
or:
MYSQL_DATABASE: krityemiyantra
Create a Normal Database User
MYSQL_USER: my_user
MYSQL_PASSWORD: my_password
Instead of connecting every PHP project using root, we create a normal application user.
That's a better habit.
Why Are We Using Port 3307?
Pay attention to this line:
ports:
- "3307:3306"
This means:
Windows Port Docker MySQL Port
3307 ---> 3306
MySQL continues listening on its normal port 3306 inside the container.
But Windows accesses it through:
3307
Docker's own MySQL + phpMyAdmin example uses this exact host-to-container port pattern.
Why do I like using 3307?
Because it keeps Docker MySQL clearly separate from any local database service that may already expect 3306.
You could technically use:
"3306:3306"
if nothing else is using 3306.
But using 3307 avoids unnecessary confusion and port conflicts.
Pro Tip: Use 3307 for Docker MySQL and forget about fighting over XAMPP's traditional database port.
The Most Important Part: Docker Volume
Now look at:
volumes:
- mysql_data:/var/lib/mysql
and:
volumes:
mysql_data:
This creates a named Docker volume.
Your actual database data lives persistently inside that volume rather than relying only on the container's writable filesystem.
Docker's database guide specifically mounts a named volume at /var/lib/mysql for persistent MySQL data.
This is extremely important.
You can run:
docker compose down
and later:
docker compose up -d
Your databases should still be there because the named volume remains.
Warning: Don't Delete the Volume Unless You Mean It
There is an important difference between:
docker compose down
and:
docker compose down -v
Be careful with:
docker compose down -v
The -v option tells Docker Compose to remove the project's named volumes as well.
That can delete the database storage associated with this Compose setup.
So for normal stopping and starting, use:
docker compose down
or simply:
docker compose stop
Don't casually delete your database volume.
Step 4: Start MySQL and phpMyAdmin
Open PowerShell, Command Prompt, or your terminal inside the folder containing docker-compose.yml.
Run:
docker compose up -d
Docker will download the images if necessary and start both containers.
Check them with:
docker compose ps
You should see your MySQL and phpMyAdmin services running.
You can also use:
docker ps
Step 5: Open phpMyAdmin
Open your browser and visit:
http://localhost:8080
You should see phpMyAdmin.
Login using:
Username: root
Password: root_password
Or use the application user:
Username: my_user
Password: my_password
The phpMyAdmin container connects directly to:
mysql:3306
Why mysql?
Because mysql is the Compose service name:
mysql:
Docker Compose automatically provides networking between services in the same project, and containers can reach one another using the service name. Docker's documentation uses this same approach.
Wait, Why Does phpMyAdmin Use 3306 but PHP Uses 3307?
This confused me initially too, so here's the simple explanation.
phpMyAdmin is inside Docker.
PHP from XAMPP is outside Docker on Windows.
Therefore:
phpMyAdmin container
|
| mysql:3306
v
MySQL container
But your XAMPP PHP application connects like this:
XAMPP PHP
|
| 127.0.0.1:3307
v
Windows
|
| port mapping
v
Docker MySQL:3306
So remember this rule:
Inside Docker:
mysql:3306
From XAMPP PHP:
127.0.0.1:3307
Once you understand that, the entire setup becomes much easier.
[Suggested Diagram: Two connection paths showing phpMyAdmin → mysql:3306 and XAMPP PHP → 127.0.0.1:3307 → MySQL container.]
Step 6: Connect Your XAMPP PHP Project to Docker MySQL
Suppose your PHP project is located here:
C:\xampp\htdocs\my-project
Instead of using:
localhost
with the default MySQL port, connect using:
Host: 127.0.0.1
Port: 3307
Database: my_database
Username: my_user
Password: my_password
PHP MySQLi Connection Example
<?php
$host = '127.0.0.1';
$port = 3307;
$database = 'my_database';
$username = 'my_user';
$password = 'my_password';
$conn = new mysqli(
$host,
$username,
$password,
$database,
$port
);
if ($conn->connect_error) {
die('Database connection failed: ' . $conn->connect_error);
}
echo 'Connected to Docker MySQL successfully!';
Save it as something like:
test-db.php
inside:
C:\xampp\htdocs\my-project\
Then open:
http://localhost/my-project/test-db.php
If everything is configured correctly, you'll see:
Connected to Docker MySQL successfully!
That PHP file is still being executed by XAMPP PHP.
The database connection simply goes to Docker.
PDO Connection Example
If you prefer PDO:
<?php
$host = '127.0.0.1';
$port = 3307;
$database = 'my_database';
$username = 'my_user';
$password = 'my_password';
$dsn = "mysql:host={$host};port={$port};dbname={$database};charset=utf8mb4";
try {
$pdo = new PDO($dsn, $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
echo 'Connected to Docker MySQL successfully!';
} catch (PDOException $e) {
die('Database connection failed: ' . $e->getMessage());
}
The important values are still:
127.0.0.1
3307
my_database
my_user
my_password
Using the Same Database in PHP and phpMyAdmin
This is another reason I like this setup.
There aren't two databases.
phpMyAdmin and your PHP application are accessing the same MySQL server and the same Docker volume.
For example, create a table using phpMyAdmin:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL
);
Then insert something from PHP.
Refresh phpMyAdmin.
The new data appears there.
That's because both applications are talking to the same MySQL container.
What If I Already Have a Database in XAMPP?
You probably don't want to lose an existing PHP project's database.
Before switching, export it.
Step 1: Start Your Old XAMPP Database One Last Time
If it still starts, open the old XAMPP phpMyAdmin.
Usually:
http://localhost/phpmyadmin
Select your database.
Choose:
Export → Quick → SQL → Export
Save the .sql file.
Step 2: Start Docker MySQL
Stop XAMPP MySQL and start your Docker environment:
docker compose up -d
Step 3: Open Docker phpMyAdmin
Visit:
http://localhost:8080
Select or create your database.
Choose:
Import
Upload your .sql file.
Now your old PHP project can use the migrated database through:
127.0.0.1:3307
Pro Tip: Always keep a separate database backup before migrating or experimenting with database storage.
Your Daily Development Workflow Becomes Very Simple
After setting this up once, my normal workflow becomes:
Start XAMPP Apache
Open XAMPP and start only:
Apache
Don't start MySQL.
Start Docker Database
From the folder containing your Compose file:
docker compose up -d
That's it.
Now you have:
PHP Website:
http://localhost/my-project
phpMyAdmin:
http://localhost:8080
MySQL:
127.0.0.1:3307
When You're Finished Working
You have two choices.
You can leave the containers running.
Or stop them:
docker compose stop
Start them again later:
docker compose start
You can also use:
docker compose down
and recreate them later:
docker compose up -d
Because the database files live in the named volume, recreating the container doesn't mean starting from an empty database.
Useful Docker Commands for This Setup
You really don't need many Docker commands.
Start Everything
docker compose up -d
Check Status
docker compose ps
Stop Containers
docker compose stop
Start Existing Containers
docker compose start
Remove Containers Without Intentionally Removing the Named Volume
docker compose down
View MySQL Logs
docker compose logs mysql
The official MySQL image documentation also recommends Docker logs as a way to inspect the MySQL server's output.
If something isn't starting correctly, this command is much more useful than randomly changing files:
docker compose logs mysql
Do I Still Need XAMPP phpMyAdmin?
No.
That's another nice part of this setup.
You can ignore XAMPP's built-in phpMyAdmin and use:
http://localhost:8080
The Docker phpMyAdmin container connects directly to Docker MySQL.
The official phpMyAdmin Docker image supports this configuration through:
PMA_HOST: mysql
PMA_PORT: 3306
Why Not Put PHP in Docker Too?
You absolutely can.
A fully containerized setup might include:
Nginx/Apache
PHP
MySQL
phpMyAdmin
Redis
inside Docker.
For larger projects, teams, or environments where consistency is critical, that can be a great setup.
But that isn't what I'm trying to solve here.
I wanted something simple.
I already liked using XAMPP for PHP development.
My frustration was the database shutdown problem.
So rather than rebuilding everything, I replaced only the part causing trouble.
Sometimes the simplest solution is the one you'll actually continue using.
Common Mistakes to Avoid
1. Starting XAMPP MySQL Anyway
Once you're using Docker MySQL, you generally don't need to start XAMPP's database service.
Your workflow becomes:
XAMPP:
Apache = ON
MySQL = OFF
Docker:
MySQL = ON
phpMyAdmin = ON
Keep it simple.
2. Using Port 3306 in Your XAMPP PHP Configuration
Remember that the Compose file exposes MySQL on Windows through:
3307
So this may fail:
$port = 3306;
Use:
$port = 3307;
for PHP running directly through XAMPP.
3. Using mysql as the Host in XAMPP PHP
This:
$host = 'mysql';
works for another container on the same Docker Compose network.
Your XAMPP PHP installation isn't inside that network.
Use:
$host = '127.0.0.1';
4. Using 127.0.0.1:3307 for Docker phpMyAdmin
phpMyAdmin is already inside Docker.
It should connect directly to:
mysql:3306
That's why our Compose file contains:
PMA_HOST: mysql
PMA_PORT: 3306
5. Running docker compose down -v Without Knowing What It Does
This deserves repeating.
Don't casually run:
docker compose down -v
when your database is important.
The volume is where your MySQL data persists.
Keep backups of anything you can't afford to lose.
6. Changing MYSQL_DATABASE and Expecting an Existing Volume to Reset
This catches beginners surprisingly often.
Environment variables such as:
MYSQL_DATABASE
MYSQL_USER
MYSQL_PASSWORD
are primarily used when MySQL initializes an empty data directory.
The official MySQL Docker image documentation notes that initialization variables don't reinitialize a database directory that already contains a database.
So if your volume already contains MySQL data, simply changing:
MYSQL_DATABASE: new_database
doesn't mean Docker will wipe everything and recreate the server.
And that's a good thing.
Is This Really a Permanent Fix for XAMPP MySQL Shutdown?
Here's how I think about it.
This doesn't magically repair every possible cause of XAMPP's "MySQL shutdown unexpectedly" error.
Instead, it removes that database service from my development workflow.
I no longer depend on XAMPP's bundled database server.
So for me, that recurring XAMPP MySQL shutdown problem effectively stopped mattering.
Could Docker MySQL itself have a problem one day?
Of course.
No software is guaranteed never to fail.
But the database is now isolated, reproducible through one Compose file, easier to inspect through container logs, and stored in a persistent Docker volume.
That's a much cleaner workflow for me.
The Entire Setup in One File
This is the part I really like.
Your database development environment can basically be represented by this single file:
services:
mysql:
image: mysql:8.4
container_name: xampp-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: my_database
MYSQL_USER: my_user
MYSQL_PASSWORD: my_password
ports:
- "3307:3306"
volumes:
- mysql_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin:latest
container_name: xampp-phpmyadmin
restart: unless-stopped
depends_on:
- mysql
environment:
PMA_HOST: mysql
PMA_PORT: 3306
ports:
- "8080:80"
volumes:
mysql_data:
Then:
docker compose up -d
And you're ready.
Your PHP remains inside XAMPP.
Your database runs through Docker.
Your database files persist in a Docker volume.
phpMyAdmin runs at port 8080.
Your PHP application connects through port 3307.
Simple.
Final Thoughts
If you've searched for the XAMPP MySQL shutdown unexpectedly error multiple times, you probably understand the frustration.
You fix it.
It works.
Then eventually you're looking at the same red error message again.
That was the point where I decided I didn't want to keep fixing the same part of my development environment.
I still use XAMPP because it makes running Apache and PHP incredibly convenient.
I simply stopped asking XAMPP to manage my database.
Now my setup is:
XAMPP = Apache + PHP
Docker = MySQL + phpMyAdmin + Database Volume
For local PHP development, I've found this combination simple, understandable, and much less frustrating.
If you're currently fighting with the XAMPP MySQL shutdown error, back up your existing database and try moving only MySQL to Docker.
You don't need to Dockerize your entire application.
Start with the part that's causing you problems.
Have you also experienced XAMPP's "MySQL shutdown unexpectedly" error? What solution have you been using to deal with it?
FAQ
How do I permanently fix XAMPP MySQL shutdown unexpectedly?
There isn't one fix for every cause of the error. One reliable alternative is to stop using XAMPP's bundled database service and run MySQL separately in Docker while continuing to use XAMPP for Apache and PHP.
Your XAMPP PHP application can then connect to Docker MySQL through 127.0.0.1:3307.
Can I use Docker MySQL with XAMPP PHP?
Yes.
Your PHP application can continue running normally through XAMPP while connecting to a MySQL container exposed on your Windows machine.
For the configuration used in this tutorial:
Host: 127.0.0.1
Port: 3307
Apache Friends also confirms that XAMPP can be used with your own database server as long as you avoid database services competing for the same port.
Will my Docker MySQL database disappear when I stop Docker?
Not if you configure persistent storage correctly.
This tutorial attaches a named Docker volume:
mysql_data:/var/lib/mysql
That volume stores the MySQL database files independently from the normal lifecycle of the container.
Still, keep proper backups of important databases.
Can I use phpMyAdmin with MySQL running in Docker?
Yes.
The official phpMyAdmin Docker image can connect to another database container by setting variables such as:
PMA_HOST: mysql
PMA_PORT: 3306
In this setup, you access it through:
http://localhost:8080
Why does PHP use port 3307 while phpMyAdmin uses 3306?
Your XAMPP PHP application runs on Windows outside Docker, so it accesses the exposed host port:
127.0.0.1:3307
phpMyAdmin runs inside the same Docker Compose network as MySQL, so it connects directly to:
mysql:3306
Do I need to Dockerize PHP too?
No.
That's the whole idea behind this setup.
You can continue using XAMPP for:
Apache + PHP
and use Docker only for:
MySQL + phpMyAdmin
It's a good way to start using Docker without completely changing your existing PHP workflow.
Projects to explore next
Source-code projects from the KritiMyantra catalogue. Check each project's details before buying.
Flask Authentication System (Free Version) - Kritim Yantra
🛠 Features✔ Google & GitHub Authentication✔ User Registration & Login✔ Profile/Dashboard Access✔ Secure & Mi...
Flask Blog CMS with Admin Dashboard — Python Source Code
A Flask-based blogging website with a built-in admin dashboard. Manage articles, images, categories, tags and featured p...
Laravel 12 + ReactJS Starter Kit: Add Multi-Language Support (Arabic, English, Spanish) with Blog CRUD
🚀 Installation & Setup Download & Extract Get the ZIP from kritimyantra.com and extract it. Includes preconfigu...
Comments
No comments yet. Be the first to comment.
Sign in to join the discussion.