Of course! Using Docker with MySQL and Python is a very common and powerful combination. It allows you to create a portable, reproducible, and isolated development environment.

Here’s a complete guide covering:
- The Goal: What we'll build.
- Project Structure: How to organize our files.
- Step 1: The Docker Compose File: To easily run both MySQL and a Python app.
- Step 2: The Python Application: A simple script to connect to the database.
- Step 3: Running Everything: The commands to bring it all to life.
- Step 4: Verifying the Data: Checking that everything worked.
- Bonus: A Web App with Flask: A more interactive example.
The Goal
We will create a Python script that connects to a MySQL database running inside a Docker container. The script will create a table, insert some data, and then read it back.
Project Structure
First, let's create a clean directory for our project.
docker-mysql-python/
├── docker-compose.yml
├── requirements.txt
└── app.py
Step 1: The Docker Compose File (docker-compose.yml)
This is the heart of our setup. docker-compose lets us define and run multi-container Docker applications with a single command. We'll define two services: one for MySQL and one for our Python application.

Create a file named docker-compose.yml and add the following content:
version: '3.8'
services:
db:
image: mysql:8.0
container_name: mysql_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
MYSQL_DATABASE: mydb
MYSQL_USER: myuser
MYSQL_PASSWORD: myuser_password
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
app:
build: .
container_name: python_app
depends_on:
- db
restart: on-failure
volumes:
- .:/app
command: python app.py
volumes:
db_data:
Explanation of the db service (MySQL):
image: mysql:8.0: Uses the official MySQL 8.0 image from Docker Hub.environment: Sets up the database on first run.MYSQL_ROOT_PASSWORD: The password for the root user.MYSQL_DATABASE: The name of the database to create (mydb).MYSQL_USER&MYSQL_PASSWORD: A new user with privileges on the new database.
ports: "3306:3306": Maps port 3306 on your host machine to port 3306 in the container, so you can connect from your local machine if needed.volumes: db_data:/var/lib/mysql: This is crucial. It creates a named volumedb_dataand persists the database data. Even if you remove thedbcontainer, your data will be safe and will be available when you recreate it.
Explanation of the app service (Python):
build: .: Tells Docker Compose to build an image from aDockerfilein the current directory.depends_on: - db: This ensures that thedbcontainer is started before theappcontainer. This gives MySQL time to initialize.volumes: .:/app: Mounts your current directory () into the/appdirectory inside the container. This means any changes you make toapp.pyon your host machine will be instantly reflected inside the container, allowing for easy development.command: python app.py: The command to run when the container starts.
Step 2: The Python Application (app.py)
This script will use the mysql-connector-python library to interact with the database.

First, create a requirements.txt file for our Python dependencies:
requirements.txt
mysql-connector-python
Now, create the app.py file. This script will:
- Connect to the MySQL database using the credentials from our
docker-compose.yml. - Create a table named
users. - Insert a few sample users.
- Query and print all users from the table.
app.py
import mysql.connector
import os
import time
# --- Configuration ---
# We use environment variables to make the script portable.
# Docker Compose will pass these to the container.
DB_HOST = os.environ.get('DB_HOST', 'db') # 'db' is the service name in docker-compose.yml
DB_USER = os.environ.get('DB_USER', 'myuser')
DB_PASSWORD = os.environ.get('DB_PASSWORD', 'myuser_password')
DB_NAME = os.environ.get('DB_NAME', 'mydb')
def wait_for_db():
"""Waits for the MySQL database to be available."""
retries = 5
while retries > 0:
try:
conn = mysql.connector.connect(
host=DB_HOST,
user=DB_USER,
password=DB_PASSWORD,
database=DB_NAME
)
print("Database connection successful!")
conn.close()
return True
except mysql.connector.Error as err:
print(f"Database not ready yet: {err}. Retrying in 5 seconds...")
retries -= 1
time.sleep(5)
return False
def main():
"""Main function to interact with the database."""
if not wait_for_db():
print("Could not connect to the database. Exiting.")
return
try:
conn = mysql.connector.connect(
host=DB_HOST,
user=DB_USER,
password=DB_PASSWORD,
database=DB_NAME
)
cursor = conn.cursor()
# --- Create Table ---
print("Creating table 'users'...")
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE
)
""")
print("Table 'users' created or already exists.")
# --- Insert Data ---
print("Inserting sample data...")
users_to_insert = [
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Charlie', 'charlie@example.com')
]
# Using executemany for efficiency
cursor.executemany("INSERT INTO users (name, email) VALUES (%s, %s)", users_to_insert)
conn.commit()
print(f"{cursor.rowcount} records inserted.")
# --- Query Data ---
print("\nFetching all users from the database:")
cursor.execute("SELECT id, name, email FROM users")
results = cursor.fetchall()
if results:
for row in results:
print(f"ID: {row[0]}, Name: {row[1]}, Email: {row[2]}")
else:
print("No users found.")
except mysql.connector.Error as err:
print(f"Error: {err}")
finally:
if 'conn' in locals() and conn.is_connected():
cursor.close()
conn.close()
print("\nDatabase connection closed.")
if __name__ == "__main__":
main()
Step 3: The Dockerfile
We need a Dockerfile to build the image for our Python application.
Dockerfile
# Use an official Python runtime as a parent image FROM python:3.9-slim # Set the working directory in the container WORKDIR /app # Copy the dependencies file and install them COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy the rest of the application code into the container COPY . . # The command to run the application (this will be overridden by docker-compose) CMD ["python", "app.py"]
Step 4: Running Everything
Now that all files are in place, open your terminal in the docker-mysql-python directory and run:
docker-compose up --build
--build: This forces Docker Compose to build theappimage from theDockerfilebefore starting the containers.
You will see the logs from both containers. The db container will start first, and the app container will wait and then start. You'll see output from your Python script as it executes.
...
db_1 | 2025-10-27T10:30:00.123456Z 0 [Note] Server socket created on IP: '::'.
app_1 | Database connection successful!
app_1 | Creating table 'users'...
app_1 | Table 'users' created or already exists.
app_1 | Inserting sample data...
app_1 | 3 records inserted.
app_1 |
app_1 | Fetching all users from the database:
app_1 | ID: 1, Name: Alice, Email: alice@example.com
app_1 | ID: 2, Name: Bob, Email: bob@example.com
app_1 | ID: 3, Name: Charlie, Email: charlie@example.com
app_1 |
app_1 | Database connection closed.
...
To stop the containers and remove the network, press Ctrl+C. To stop and remove everything (including the data volume), run:
docker-compose down -v
Step 5: Verifying the Data Persistence
This is a great way to see the power of Docker volumes.
- Run
docker-compose up -dto start the containers in the background. - Run
docker-compose down -v. This will stop the containers and delete the named volumedb_data. - Run
docker-compose up -dagain. - Check the logs of the
appcontainer (docker-compose logs app).
You will notice that the script prints "Table 'users' created or already exists" but no "Inserting sample data..." message. This is because the users table and its data still exist from the previous run, thanks to the db_data volume. The Python script correctly detects that the table already exists and skips the INSERT statement.
Bonus: A Web App with Flask and SQLAlchemy
For a more realistic example, let's create a simple Flask web app that exposes an API to manage users.
Update requirements.txt
mysql-connector-python
Flask
SQLAlchemy
Create web_app.py
import os
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
import time
# --- Configuration ---
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Use environment variables for the database URI
DB_USER = os.environ.get('DB_USER', 'myuser')
DB_PASSWORD = os.environ.get('DB_PASSWORD', 'myuser_password')
DB_HOST = os.environ.get('DB_HOST', 'db')
DB_NAME = os.environ.get('DB_NAME', 'mydb')
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+mysqlconnector://{DB_USER}:{DB_PASSWORD}@{DB_HOST}/{DB_NAME}"
db = SQLAlchemy(app)
# --- Model ---
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def to_dict(self):
return {'id': self.id, 'name': self.name, 'email': self.email}
# --- Routes ---
@app.route('/users', methods=['GET'])
def get_users():
users = User.query.all()
return jsonify([user.to_dict() for user in users])
@app.route('/users', methods=['POST'])
def create_user():
data = request.get_json()
if not data or 'name' not in data or 'email' not in data:
return jsonify({'error': 'Missing name or email'}), 400
new_user = User(name=data['name'], email=data['email'])
try:
db.session.add(new_user)
db.session.commit()
return jsonify(new_user.to_dict()), 201
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
# --- Main ---
def wait_for_db():
retries = 10
while retries > 0:
try:
db.create_all() # Attempt to create tables
print("Database connection and table setup successful!")
return True
except Exception as e:
print(f"Database not ready yet: {e}. Retrying in 5 seconds...")
retries -= 1
time.sleep(5)
return False
if __name__ == '__main__':
if wait_for_db():
app.run(host='0.0.0.0', port=5000, debug=True)
Update docker-compose.yml
We need to expose the Flask app's port and change the command.
version: '3.8'
services:
db:
image: mysql:8.0
container_name: mysql_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
MYSQL_DATABASE: mydb
MYSQL_USER: myuser
MYSQL_PASSWORD: myuser_password
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
app:
build: .
container_name: flask_app
depends_on:
- db
restart: on-failure
ports:
- "5000:5000" # Expose Flask port 5000
volumes:
- .:/app
command: python web_app.py # Change command here
volumes:
db_data:
Run and Test
docker-compose up --build
Once it's running, open another terminal and use curl to test the API:
# Get all users (should be empty at first)
curl http://localhost:5000/users
# Create a new user
curl -X POST -H "Content-Type: application/json" -d '{"name": "David", "email": "david@example.com"}' http://localhost:5000/users
# Get all users again (should now include David)
curl http://localhost:5000/users
This example shows how to integrate a Python web framework with a database running in Docker, a very common pattern in modern development.
