This Docker Compose stack deploys 5 WordPress websites with automatic SSL certificate management using Let's Encrypt and Nginx reverse proxy.
- 🚀 5 Independent WordPress Sites - Each with its own database and configuration
- 🔒 Automatic SSL Certificates - Let's Encrypt integration for free SSL certificates
- 🌐 Nginx Reverse Proxy - Efficient load balancing and SSL termination
- 🗄️ MySQL 8.0 Database - Consolidated database hosting all WordPress sites
- 📊 phpMyAdmin - Web-based database management interface
- 🔧 Easy Configuration - Environment-based configuration
- 📈 Performance Optimized - Caching, compression, and security headers
- ⏱️ Kimai Time Tracking - Integrated time tracking system
- 💾 Hybrid Storage - WordPress core in Docker volumes, user content in bind mounts
Internet → Nginx Proxy (80/443) → Let's Encrypt → WordPress Sites → MySQL Database
↓ ↓
Auto Config SSL Certificates
The stack uses:
- nginx-proxy: Main reverse proxy with automatic configuration
- letsencrypt: Manages Let's Encrypt SSL certificates automatically
- mysql: Consolidated MySQL 8.0 database hosting all WordPress sites
- kimai: Time tracking system with separate database
- Docker and Docker Compose installed
- Domain names pointing to your server
- Ports 80 and 443 available
-
Clone and navigate to the project:
cd multi-wordpress-deployment -
Copy the environment file:
cp sample.env .env
-
Edit the environment file:
nano .env
Update the following variables:LETSENCRYPT_EMAIL- Your email for Let's Encrypt notificationsWP1_DOMAIN,WP2_DOMAIN,WP3_DOMAIN,WP4_DOMAIN,WP5_DOMAIN- Your actual domain namesMYSQL_ROOT_PASSWORD- Secure MySQL root password- Database passwords for each WordPress site
- Kimai configuration (if using time tracking)
- Resource limits (WP_MEMORY, WP_CPUS, KIMAI_MEMORY) for your VM specs
-
Create required directories:
mkdir -p certs logs/nginx vhost.d html
-
Start the stack:
docker compose up -d
-
Check the status:
docker compose ps
| Variable | Description | Example |
|---|---|---|
LETSENCRYPT_EMAIL |
Email for Let's Encrypt notifications | admin@example.com |
MYSQL_ROOT_PASSWORD |
MySQL root password | secure_password_123 |
WP1_DOMAIN |
Domain for WordPress site 1 | site1.example.com |
WP1_DB_NAME |
Database name for site 1 | wordpress1 |
WP1_DB_USER |
Database user for site 1 | wp1_user |
WP1_DB_PASSWORD |
Database password for site 1 | wp1_password |
WP1_TABLE_PREFIX |
WordPress table prefix for site 1 | wp1_ |
WP5_DOMAIN |
Domain for WordPress site 5 | site5.example.com |
WP5_DB_NAME |
Database name for site 5 | wordpress5 |
WP5_DB_USER |
Database user for site 5 | wp5_user |
WP5_DB_PASSWORD |
Database password for site 5 | wp5_password |
WP5_TABLE_PREFIX |
WordPress table prefix for site 5 | wp5_ |
KIMAI_DOMAIN |
Domain for Kimai time tracking | kimai.example.com |
KIMAI_DB_NAME |
Database name for Kimai | kimai |
KIMAI_DB_USER |
Database user for Kimai | kimai_user |
KIMAI_DB_PASSWORD |
Database password for Kimai | kimai_password |
Make sure your domain names point to your server's IP address:
# Example DNS records
site1.example.com. A YOUR_SERVER_IP
site2.example.com. A YOUR_SERVER_IP
site3.example.com. A YOUR_SERVER_IP
site4.example.com. A YOUR_SERVER_IP
site5.example.com. A YOUR_SERVER_IP
kimai.example.com. A YOUR_SERVER_IP
pma.example.com. A YOUR_SERVER_IP- wordpress1 - First WordPress site
- wordpress2 - Second WordPress site
- wordpress3 - Third WordPress site
- wordpress4 - Fourth WordPress site
- wordpress5 - Fifth WordPress site
- mysql - Consolidated MySQL 8.0 database hosting all WordPress sites and Kimai
- nginx-proxy - Nginx reverse proxy with automatic configuration
- letsencrypt - Automatic SSL certificate management
- kimai - Time tracking system
- phpmyadmin - Database management interface
After deployment, you can access:
- WordPress Site 1:
https://site1.example.com - WordPress Site 2:
https://site2.example.com - WordPress Site 3:
https://site3.example.com - WordPress Site 4:
https://site4.example.com - WordPress Site 5:
https://site5.example.com - Kimai Time Tracking:
https://kimai.example.com - phpMyAdmin:
https://pma.example.com
SSL certificates are automatically managed by Let's Encrypt:
- Certificates are automatically renewed
- HTTP traffic is redirected to HTTPS
- HSTS headers are enabled for security
- Rate Limiting - Protection against brute force attacks
- Security Headers - XSS protection, content type sniffing prevention
- SSL/TLS - Modern cipher suites and protocols
- Isolated Networks - Each service runs in its own network namespace
The deployment includes comprehensive backup and restore scripts:
# Navigate to deployment directory
cd multi-wordpress-deployment
# Run manual backup
./wp-backup.sh
# Restore specific site
./wp-restore.sh restore wp3 /var/backups/wordpress/wordpress_wp3_20251026_223848.tar.gz files
# Restore database only
./wp-restore.sh restore wp3 /var/backups/wordpress/wordpress_wp3_20251026_223848.tar.gz database
# Full system restore (all databases)
./wp-restore.sh restore wp3 /var/backups/wordpress/wordpress_wp3_20251026_223848.tar.gz full-db
# Setup automated backups
./setup-backup-cron.sh -d # Daily backups- Individual WordPress Databases - Each site's database separately
- Full Database Backup - Complete system backup including all databases
- WordPress Files - Plugins, themes, uploads (bind-mounted directories)
- Kimai Data - Time tracking data and configuration
- Configuration Files - docker-compose.yml, .env, SSL certificates
- Docker Volumes - MySQL data, Kimai data
- WordPress Core - Stored in Docker volumes (wp1_data, wp2_data, etc.)
- User Content - Plugins, themes, uploads stored in bind-mounted directories (
wp/wp1/,wp/wp2/, etc.) - Easy Backup - User content directly accessible for backup and restore
# View Nginx logs
docker compose logs nginx-proxy
# View WordPress logs
docker compose logs wordpress1
docker compose logs wordpress2
docker compose logs wordpress3
docker compose logs wordpress4
docker compose logs wordpress5
# View Kimai logs
docker compose logs kimai
# View MySQL logs
docker compose logs mysql# Check Let's Encrypt logs
docker compose logs letsencrypt
# Force certificate renewal
docker compose restart letsencrypt# Check database status
docker compose ps mysql
# Connect to database
docker compose exec mysql mysql -u root -p# Check Nginx configuration
docker compose exec nginx-proxy nginx -t
# Reload Nginx configuration
docker compose exec nginx-proxy nginx -s reloadThe stack is optimized for a 2-core 4GB VM with the following resource allocation:
- WordPress containers: 512MB each (5 × 512MB = 2.5GB)
- MySQL database: 1GB
- Kimai: 512MB
- Infrastructure: ~200MB (nginx, letsencrypt, phpmyadmin)
- System overhead: ~800MB reserved
- WordPress containers: 0.5 cores each (5 × 0.5 = 2.5 cores)
- MySQL database: 0.5 cores
- Kimai: 0.25 cores
- Infrastructure: Minimal CPU usage
- Log retention: 10MB per container, max 3 files
- Automatic rotation: Prevents disk space issues
The stack includes several performance optimizations:
- Gzip Compression - Reduces bandwidth usage
- Static File Caching - Improves load times
- Database Optimization - MySQL tuned for WordPress
- PHP Optimization - Increased memory and upload limits
- Resource Limits - Prevents container resource exhaustion
To add more WordPress sites:
- Add new services to
docker-compose.yml - Add corresponding environment variables to
.env - Update DNS records
- Restart the stack
-
4th WordPress site not accessible:
# Run the debug script ./debug.sh # Check if docker-gen generated the config docker-compose exec nginx-proxy cat /etc/nginx/conf.d/default.conf # Restart docker-gen docker-compose restart docker-gen
-
SSL certificate issues:
# Check Let's Encrypt logs docker-compose logs letsencrypt # Verify DNS propagation nslookup your-domain.com
-
Resource exhaustion:
# Check resource usage docker stats # Adjust limits in .env file WP_MEMORY=256M # Reduce if needed DB_MEMORY=256M # Reduce if needed
-
Container startup issues:
# Check all logs docker-compose logs # Restart specific service docker-compose restart [service-name]
For issues and questions:
- Run the debug script:
./debug.sh - Check the logs:
docker-compose logs [service-name] - Verify DNS resolution
- Ensure ports 80 and 443 are open
- Check firewall settings
- Review resource limits in
.envfile
This project is licensed under the MIT License - see the LICENSE file for details.