
Introduction
When teams begin using Docker, they often face two common problems.
First, database data disappears after container restarts.
Second, applications sometimes fail to connect to databases.
Because of these issues, beginners often feel that Docker is unreliable.
However, the real problem is missing configuration, not Docker itself.
In this blog, we will solve both issues step by step.
We will deploy a PHP application with a MySQL database using Docker Compose, Docker volumes, and Docker networks.
Even if you are new to DevOps, you will be able to follow along.
Problem Statement
A company wants to deploy a simple PHP web application that stores and retrieves data from a MySQL database, but during testing, the team encounters several issues. The database data disappears whenever containers restart, and at times the PHP application fails to connect to MySQL. These problems arise because containers are temporary by nature, no persistent storage has been configured for the database, and the containers are not connected through a defined network. As a result, the application behaves unpredictably and does not meet reliability expectations for a production environment.
Why does this happen?
In the current setup, the MySQL data is stored directly inside the container, which is problematic because containers are ephemeral and can be destroyed and recreated at any time. Additionally, no persistent storage in the form of Docker volumes has been configured, and the containers are not connected using a defined network. Our task is to address all of these issues by using Docker Compose to deploy the PHP application with MySQL in a more reliable, persistent, and well-structured manner.
Solution Overview
We will:
- Run PHP and MySQL in separate containers
- Use Docker volumes to persist MySQL data
- Use a Docker network for secure container communication
- Use Docker Compose to manage everything together
Architecture Diagram (Conceptual)
Browser
|
v
PHP Container ---> MySQL Container
| |
| Docker Volume
| (Persistent Data)
|
Docker NetworkProject Structure
php-mysql-docker/
│
├── docker-compose.yml
└── php/
└── index.php
Create the PHP Application
Create a folder named php and inside it create index.php.
php/index.php:
<?php
$host = getenv("DB_HOST");
$user = getenv("DB_USER");
$pass = getenv("DB_PASSWORD");
$db = getenv("DB_NAME");
$conn = new mysqli($host, $user, $pass, $db);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$conn->query("CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
)");
if (isset($_POST['name'])) {
$name = $_POST['name'];
$conn->query("INSERT INTO users (name) VALUES ('$name')");
}
$result = $conn->query("SELECT * FROM users");
?>
<!DOCTYPE html>
<html>
<head>
<title>PHP MySQL Docker</title>
</head>
<body>
<h2>Add User</h2>
<form method="post">
<input type="text" name="name" required>
<button type="submit">Save</button>
</form>
<h2>Saved Users</h2>
<ul>
<?php while($row = $result->fetch_assoc()): ?>
<li><?php echo $row['name']; ?></li>
<?php endwhile; ?>
</ul>
</body>
</html>Create Docker Compose Configuration
Create docker-compose.yml in the root directory.
docker-compose.yml:
version: '3.8'
services:
php:
image: php:8.1-apache
container_name: php_app
ports:
- "8080:80"
volumes:
- ./php:/var/www/html
environment:
DB_HOST: mysql
DB_USER: root
DB_PASSWORD: rootpass
DB_NAME: appdb
depends_on:
- mysql
networks:
- app_network
mysql:
image: mysql:8.0
container_name: mysql_db
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: appdb
volumes:
- mysql_data:/var/lib/mysql
networks:
- app_network
volumes:
mysql_data:
networks:
app_network:Why Docker Volumes Are Important
Docker volumes are important because they solve the problem of data persistence in containerized applications. Without volumes, MySQL data is stored inside the container itself, which means the data is lost as soon as the container is deleted or recreated. With Docker volumes, the data is stored outside the container on the host system, allowing containers to be stopped, restarted, or replaced without affecting the stored data. This separation of application lifecycle and data lifecycle is essential for building reliable and production-ready applications.
Here we use:
volumes:
- mysql_data:/var/lib/mysqlWhy Docker Networks Are Important
Docker networks allow containers to:
- Communicate securely
- Refer to each other using service names
In our PHP app:
$host = getenv("DB_HOST"); // mysqlThe hostname mysql works because:
- Both containers are on the same Docker network
Run the Application
From the project root directory, run:
docker-compose up -dCheck running containers:
docker psAccess the Application
Open your browser:
http://localhost:8080You should see:
- A form to add users
- A list of saved users
Verify Data Persistence
Add some names
Stop containers:
docker-compose down- Start again:
docker-compose up -d
The data will still be there
This confirms Docker volume persistence
Verify Container Communication
Check PHP logs:
docker logs php_appCheck MySQL logs:
docker logs mysql_dbNo connection errors = successful container networking 🎉
Common Beginner Mistakes (Avoid These)
Using localhost for DB connection
Not using volumes for databases
Hardcoding credentials
Running everything in one container
Expected Learning Outcomes
After completing this project, you’ll understand:
- How Docker Compose manages multi-container applications
- How Docker volumes prevent data loss
- How Docker networks enable container communication
- How real-world PHP + MySQL apps are deployed
- How this setup works on AWS Free Tier
Conclusion
This project demonstrates a real-world DevOps scenario implemented using Docker Compose, where multiple services work together to form a complete application. By separating the application and database into independent services, adding persistent storage through Docker volumes, and defining dedicated networks for communication, the setup becomes far more robust and predictable. These practices ensure improved reliability by preventing data loss, scalability by allowing services to be managed and extended independently, and production-ready behavior that closely mirrors real deployment environments. For anyone preparing for DevOps interviews, this type of Docker Compose–based architecture is commonly discussed and frequently tested, making this project both practical and highly relevant.


