Production-grade Docker Compose stack untuk hosting multiple PHP projects pada satu server.
Target host: 24 CPU, 96GB RAM (AMD EPYC 7282)
┌─────────────┐
:80 │ Nginx │
───────────────►│ (Alpine) │
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ PHP 8.4 │ │ RustFS │ │Gotenberg │
│ FPM │ │ (S3) │ │ (PDF) │
│ :9000 │ │:9000/9001│ │ :3000 │
└────┬─────┘ └──────────┘ └──────────┘
│
┌─────┴─────┐
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ MySQL │ │ Redis │
│ 8.4 │ │ (Alpine) │
│ :3306 │ │ :6379 │
└──────────┘ └──────────┘
| Service | Image | Port(s) | Fungsi |
|---|---|---|---|
| Nginx | nginx:alpine |
80 | Reverse proxy & static files |
| PHP 8.4 | Custom (FPM) | 9000 | PHP application server |
| MySQL | mysql:8.4 |
3306 | Database utama |
| Redis | redis:alpine |
6379 | Cache & session store |
| RustFS | rustfs/rustfs |
9000, 9001 | S3-compatible object storage |
| Gotenberg | gotenberg |
3000 | HTML-to-PDF conversion |
docker-webstack/
├── docker-compose.yml
├── .env # Credentials (gitignored)
├── docker/
│ ├── nginx/
│ │ ├── nginx.conf # Config utama Nginx
│ │ └── conf.d/
│ │ ├── optimization.conf # Gzip, buffers, caching
│ │ └── rustfs.conf # Proxy ke RustFS console & S3 API
│ ├── php/8.4/
│ │ ├── Dockerfile # PHP extensions & Composer
│ │ ├── php.ini # OPcache, JIT, session via Redis
│ │ ├── www.conf # FPM pool (dynamic, max 200 workers)
│ │ └── zz-docker.conf # FPM daemon config
│ ├── mysql/
│ │ └── my.cnf # InnoDB tuning untuk 96GB RAM
│ └── redis/
│ └── redis.conf # 4GB maxmemory, AOF + RDB persistence
├── data/ # Volume data (gitignored)
│ ├── mysql/
│ ├── redis/
│ └── rustfs/
└── projects/ # PHP projects (gitignored)
└── <project>/
└── nginx.conf # Vhost auto-loaded oleh Nginx
git clone <repo-url> && cd docker-webstackBuat .env dari contoh:
cat > .env << 'EOF'
MYSQL_ROOT_PASSWORD=<password>
MYSQL_DATABASE=<database>
MYSQL_USER=<user>
MYSQL_PASSWORD=<password>
RUSTFS_ACCESS_KEY=<access_key>
RUSTFS_SECRET_KEY=<secret_key>
EOFdocker compose up -dLetakkan project PHP dalam projects/<nama-project>/. Buat nginx.conf di root project tersebut — ia akan di-load secara automatik oleh Nginx.
Contoh vhost (projects/example.com/nginx.conf):
server {
listen 80;
server_name example.com;
root /var/www/projects/example.com/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php84:9000;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
}Kemudian reload Nginx:
docker compose exec nginx nginx -s reload- OPcache enabled dengan JIT tracing (256MB buffer)
- Session disimpan dalam Redis (
tcp://redis:6379) - FPM Pool: dynamic, 20 start / max 200 workers
- Extensions: pdo_mysql, redis, gd, intl, zip, bcmath, pcntl, sockets, exif, opcache
- Upload limit: 256MB
- InnoDB buffer pool: 16GB (8 instances)
- Log file: 1GB
- Max connections: 500
- Slow query log: enabled (> 2s)
- Charset: utf8mb4
- Max memory: 4GB (allkeys-lru eviction)
- Persistence: AOF (everysec) + RDB snapshots
- IO threads: 4
- Worker connections: 8,192
- Gzip: level 6, min 256 bytes
- FastCGI timeout: 300s
- Upload limit: 256MB
- Auto-vhost: loads
projects/*/nginx.conf
- Console: port 9001 (proxied via
rustfs.okdii.com) - S3 API: port 9000 (proxied via
/s3/path)
| Service | CPU Limit | Memory Limit | CPU Reserved | Memory Reserved |
|---|---|---|---|---|
| Nginx | 4 | 2GB | 1 | 256MB |
| PHP 8.4 | 8 | 16GB | 2 | 1GB |
| MySQL | 8 | 24GB | 2 | 4GB |
| Redis | 4 | 6GB | 1 | 512MB |
| RustFS | 2 | 4GB | 0.5 | 256MB |
| Gotenberg | 2 | 2GB | 0.5 | 256MB |
| Total | 28 | 54GB | 7 | 6.25GB |
# Start semua services
docker compose up -d
# Stop semua
docker compose down
# Rebuild PHP image (selepas edit Dockerfile)
docker compose build php84 && docker compose up -d php84
# Logs
docker compose logs -f <service>
# Masuk container
docker compose exec php84 bash
docker compose exec mysql mysql -u root -p
# Reload Nginx config
docker compose exec nginx nginx -s reload