Running databases in Docker is a fast way to spin up development environments and is increasingly common in production when combined with proper volumes, backups, and resource limits. This guide covers all three of the most common database engines.
Prerequisites
- Docker Engine and Docker Compose installed
- Root or sudo access
MySQL
services:
mysql:
image: mysql:8.0
container_name: mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
Connect from the host:
docker exec -it mysql mysql -u root -p
PostgreSQL
services:
postgres:
image: postgres:16
container_name: postgres
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Connect from the host:
docker exec -it postgres psql -U postgres
Redis
services:
redis:
image: redis:7
container_name: redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
redis_data:
Connect from the host:
docker exec -it redis redis-cli -a YOUR_REDIS_PASSWORD
Should You Publish Database Ports Publicly?
Generally, no. If only other containers in the same Compose project need access, omit the ports: section entirely — they can still reach the database by service name over the internal Compose network. Only publish a port if you specifically need external access, and restrict it at the firewall level.
Backing Up Each Database
MySQL:
docker exec mysql mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --all-databases > backup.sql
PostgreSQL:
docker exec postgres pg_dumpall -U postgres > backup.sql
Redis (triggers an RDB snapshot):
docker exec redis redis-cli -a YOUR_PASSWORD SAVE
Restoring MySQL
cat backup.sql | docker exec -i mysql mysql -u root -p"$MYSQL_ROOT_PASSWORD"
Common Errors
Data lost after docker compose down — confirm a named volume is configured; without one, data lives only in the container's writable layer and is lost on removal.
"Connection refused" from another container — verify both services are on the same Compose project/network, and connect using the service name (e.g. mysql), not localhost.
Best Practices
- Always use named volumes for database data
- Set strong passwords via a
.envfile, never hardcoded - Avoid publishing database ports publicly unless absolutely necessary
- Schedule regular automated dumps as part of your backup routine
FAQ
Is running a production database in Docker a good idea?
It's widely done successfully, provided you use persistent volumes, resource limits, regular backups, and don't expose the database port to the public internet.
Related Articles
- Docker Volumes: Persisting Data
- Docker Compose .env Files and Environment Variables Explained
- How to Set Up Automated VPS Backups
