Introduction
Manually installing and configuring middleware like MySQL, Redis, and object storage for local development often leads to environment inconsistencies and version management issues. Docker Compose lets you spin up a reproducible development environment instantly with a single docker compose up command.
This article explains how to build a practical development environment with MySQL, Redis, and MinIO (S3-compatible object storage) using Docker Compose.
Basic docker-compose.yml Structure
A Docker Compose configuration file consists of three main sections:
services: # Container definitions
web:
image: nginx
ports:
- "8080:80"
volumes: # Volumes for data persistence
db-data:
networks: # Custom networks
backend:
Service Configuration
MySQL
services:
mysql:
image: mysql:8.0
container_name: dev-mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpass}
MYSQL_DATABASE: ${MYSQL_DATABASE:-devdb}
MYSQL_USER: ${MYSQL_USER:-devuser}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-devpass}
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
command: >
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--default-time-zone=+09:00
Setting default-time-zone=+09:00 enables JST-based datetime management.
Redis
redis:
image: redis:7-alpine
container_name: dev-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
command: redis-server --appendonly yes
--appendonly yes enables AOF (Append Only File) persistence for data durability.
MinIO (S3-Compatible Storage)
minio:
image: minio/minio:latest
container_name: dev-minio
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
ports:
- "9000:9000" # API
- "9001:9001" # Console
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 10s
timeout: 5s
retries: 5
command: server /data --console-address ":9001"
MinIO provides an AWS S3-compatible API, making it ideal for local development of applications that use S3 in production.
Complete docker-compose.yml
services:
mysql:
image: mysql:8.0
container_name: dev-mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpass}
MYSQL_DATABASE: ${MYSQL_DATABASE:-devdb}
MYSQL_USER: ${MYSQL_USER:-devuser}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-devpass}
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
command: >
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--default-time-zone=+09:00
redis:
image: redis:7-alpine
container_name: dev-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
command: redis-server --appendonly yes
minio:
image: minio/minio:latest
container_name: dev-minio
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 10s
timeout: 5s
retries: 5
command: server /data --console-address ":9001"
volumes:
mysql-data:
redis-data:
minio-data:
Common Commands
| Command | Description |
|---|---|
docker compose up -d | Start in background |
docker compose down | Stop and remove containers |
docker compose down -v | Stop and remove volumes too |
docker compose logs -f mysql | Follow MySQL logs |
docker compose exec mysql mysql -u devuser -p devdb | Connect to MySQL |
docker compose exec redis redis-cli | Connect to Redis CLI |
docker compose ps | Check container status |
docker compose restart redis | Restart specific service |
Data Persistence
Named Volumes vs Bind Mounts
| Type | Use Case | Example |
|---|---|---|
| Named Volume | DB data persistence | mysql-data:/var/lib/mysql |
| Bind Mount | Source code sync | ./src:/app/src |
Named Volumes persist after docker compose down, but are deleted with the -v flag.
Health Checks and depends_on
Combine healthcheck with depends_on to control service startup order:
services:
app:
build: .
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
condition: service_healthy waits for the dependency’s health check to pass before starting the app.
Environment Variable Management
A .env file placed alongside docker-compose.yml is automatically loaded:
# .env
MYSQL_ROOT_PASSWORD=my_secure_password
MYSQL_DATABASE=myapp
MYSQL_USER=appuser
MYSQL_PASSWORD=app_secure_password
MINIO_ROOT_USER=minio_admin
MINIO_ROOT_PASSWORD=minio_secure_password
Add .env to .gitignore and include a .env.example (without values) in version control.
Tips
Selective Service Startup with Profiles
Not all services are always needed. Profiles enable selective startup:
services:
minio:
profiles: ["storage"]
image: minio/minio:latest
# ...
docker compose up -d # mysql, redis only
docker compose --profile storage up -d # include minio
Volume Cleanup
Unused volumes accumulate and consume disk space:
docker volume ls # List volumes
docker volume prune # Remove unused volumes
docker system df # Docker disk usage
Related Articles
- Kubernetes Basics - Kubernetes fundamentals as the next step in container orchestration.
- GitHub Actions Basics - Integrating Docker Compose environments into CI/CD pipelines.
- Transaction Isolation Levels and Data Inconsistencies - Detailed MySQL transaction isolation levels.
- Prisma and MySQL Timezone Mismatch - Timezone issues commonly encountered with MySQL on Docker.