Enfection Production Docker Stack
A production-ready Docker Compose stack for WordPress with CI/CD automation, Redis caching, optimized Nginx, and Let's Encrypt SSL.
Stack Overview
GitHub Actions CI/CD
↓
Lightsail Ubuntu 24.04
└── Docker Compose
├── MariaDB 10.11 — database
├── Redis 7 Alpine — object cache
├── WordPress PHP 8.3 — application (PHP-FPM)
└── Nginx Stable — web server (port 80/443)
What's Included
| Component | Purpose |
|---|---|
MariaDB 10.11 |
Database — 20-30% faster than MySQL 8.0 |
WordPress 6.7 PHP 8.3 FPM |
Application server |
Redis 7 Alpine |
Object caching |
Nginx Stable Alpine |
Web server with gzip, security headers, rate limiting |
Certbot |
Let's Encrypt SSL automation (production domain) |
PHP uploads.ini |
256MB memory, 64MB uploads, 300s timeout |
Nginx Optimizations
- Gzip compression — reduces page size 3x
- Security headers — X-Frame-Options, X-XSS-Protection, X-Content-Type-Options
- Rate limiting — wp-login.php max 5 requests/min (brute force protection)
- Static file caching — 30 days + Cache-Control immutable
- FastCGI buffers — 16x16k, faster PHP response handling
- Blocked — xmlrpc.php, PHP execution in uploads/
Structure
docker/
├── docker-compose.yml — full stack definition
├── nginx.conf — optimized nginx config
└── php/
└── uploads.ini — PHP tuning
.github/workflows/
├── deploy.yml — staging CI/CD (SonarQube + deploy)
└── deploy-production.yml — production CI/CD (Lightsail Docker)
Prerequisites
- AWS Lightsail instance (Ubuntu 24.04 LTS, $10/month, 2GB RAM)
- Docker + Docker Compose installed on server
- GitHub repository with Actions secrets configured
- Ports 80 and 443 open in Lightsail firewall
Lightsail Server Setup (First Time Only)
1. Create instance:
#!/bin/bash
apt-get update -y
apt-get install -y docker.io docker-compose
systemctl enable docker
systemctl start docker
usermod -aG docker ubuntu
→ Networking → Create Static IP → attach
→ Snapshots → Enable automatic snapshots
→ Firewall → Add HTTP (80) + HTTPS (443) rules
2. Download SSH key:
Lightsail → Account → SSH keys
→ Download: LightsailDefaultKey-ap-south-1.pem
→ Save as: C:\Users\chamo\.ssh\lightsail-production
Tip
If .pem starts with -----BEGIN RSA PRIVATE KEY----- it's already
OpenSSH format. Use directly — no PuTTYgen conversion needed.
3. Set GitHub secrets:
gh secret set PROD_SERVER_IP --body "43.x.x.x" --repo Enfection-Dev-Lab/project-wordpress-theme
gh secret set PROD_SSH_KEY --body "$(cat ~/.ssh/lightsail-production)" --repo Enfection-Dev-Lab/project-wordpress-theme
First Deploy
git checkout production
git merge staging
git push origin production
# → GitHub Actions deploys → Docker stack starts automatically ✅
Wait for Actions ✅ green (~3-5 minutes).
WordPress Setup (After First Deploy)
http://[lightsail-static-ip]/wp-admin/install.php
Site Title: [Client] Website
Username: admin
Password: (strong — save it!)
Email: devops@enfection.com
→ Install WordPress
Note
Theme is activated automatically by the deploy workflow.
SSL Setup (When Domain is Ready)
After DNS is pointed to Lightsail static IP:
1. SSH into server:
2. Install Certbot:
3. Get certificate:
sudo certbot certonly --webroot \
-w /var/www/html \
-d yourdomain.com \
-d www.yourdomain.com \
--email devops@enfection.com \
--agree-tos --non-interactive
4. Update nginx.conf for HTTPS and restart:
5. Auto-renewal (cron):
echo "0 3 * * * certbot renew --quiet && docker-compose -f /home/ubuntu/[client]/docker-compose.yml restart nginx" | sudo crontab -
Subsequent Deploys
Every push to production branch automatically:
- Copies theme files to server
- Updates docker configs (nginx.conf, docker-compose.yml, php/uploads.ini)
- Runs
docker-compose up -d --remove-orphans— picks up new services - Restarts nginx — applies latest nginx.conf
- Activates theme via WP-CLI
- Flushes WordPress cache
Branch Strategy
| Branch | Trigger | Result |
|---|---|---|
staging |
Developer push | SonarQube scan + staging deploy |
production |
DevOps after client approval | Lightsail Docker deploy |
main |
Safety backup | No deploy |
Docker Stack — Container Details
MariaDB 10.11
volume: db_data (persistent)
network: wp_network (internal)
Redis 7 Alpine
network: wp_network (internal)
WordPress 6.7 PHP 8.3 FPM
volume: wp_data + theme bind mount
php: 256MB memory, 64MB uploads, 300s timeout
network: wp_network
Nginx Stable Alpine
ports: 80, 443
volume: wp_data (read-only)
network: wp_network
Persistence
- MariaDB data — named volume
[client]_db_data - WordPress files — named volume
[client]_wp_data - SSL certificates — bind mount
./certbot/conf/ - nginx config — bind mount
./nginx.conf
Warning
Never run docker-compose down -v on production with live data.
Use docker-compose down (without -v) to preserve volumes.
Troubleshooting
# Check all containers
docker-compose ps
# Nginx logs
docker-compose logs nginx --tail=50
# WordPress logs
docker-compose logs wordpress --tail=50
# Database logs
docker-compose logs db --tail=50
# Validate nginx config
docker exec [client]_nginx_1 nginx -t
# WordPress CLI
docker exec [client]_wordpress_1 wp --info --allow-root
Common issues:
| Symptom | Cause | Fix |
|---|---|---|
File not found. |
nginx \$uri escaping bug |
Fix nginx.conf: \$uri → $uri |
ERR_CONNECTION_REFUSED |
Port 80 not open | Lightsail firewall → add HTTP rule |
| DB container exit | MySQL/MariaDB volume incompatibility | docker-compose down -v && docker-compose up -d |
| Theme not activating | WP not installed yet | Complete install.php first |
PHP Version Compatibility
| PHP | WordPress | Recommendation |
|---|---|---|
| 8.3 | 6.8+ | ✅ Best choice (current default) |
| 8.2 | 6.5+ | ✅ Safe choice |
| 8.1 | 6.4+ | Safe choice |
| 8.0 | 6.4+ | Acceptable, older |
To change PHP version — update docker/docker-compose.yml: