Host multiple WordPress sites on a single low-cost server with auto-SSL, hardened PHP, and object caching.
Internet → Caddy (auto-SSL) → OpenLiteSpeed (LSPHP) → MariaDB
↕
Redis (object cache)
| Service | Image | Purpose |
|---|---|---|
| Caddy | caddy:2-alpine | Reverse proxy, automatic Let’s Encrypt SSL |
| OpenLiteSpeed | Ubuntu 24.04 + OLS | PHP processing with LSCache |
| MariaDB | mariadb:11.4 | Database (internal network only) |
| Redis | redis:7-alpine | Object cache (internal network only) |
Recommended: AWS Lightsail ARM 1GB ($3.50/mo) or Hetzner CAX11 ($4.10/mo).
git clone https://github.com/jmcoimbra/LAMP-env.git
cd LAMP-env
cp .env.example .env
Edit .env:
MARIADB_ROOT_PASSWORD=<generate-a-strong-password>
ACME_EMAIL=you@example.com
PHP_VERSION=8.3
Start the stack:
docker compose -f docker-compose.prod.yml up -d
./scripts/add-site.sh yourdomain.com
The script:
wp-config.php with unique salts and hardening optionsPoint your DNS A record to the server’s IP. SSL provisions automatically once DNS resolves.
Run add-site.sh again for each domain. All sites share the same server resources.
| Sites | Recommended Instance | Monthly Cost | Per Site |
|---|---|---|---|
| 1-5 | 1GB ARM ($3.50) | ~$4.50 | $0.90-$4.50 |
| 5-15 | 2GB ARM ($7) | ~$8 | $0.53-$1.60 |
| 15-30 | 4GB ARM ($14) | ~$15 | $0.50-$1.00 |
The production stack includes these hardening measures out of the box:
Every response includes:
X-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINReferrer-Policy: strict-origin-when-cross-originStrict-Transport-Security: max-age=31536000X-XSS-Protection: 1; mode=blockxmlrpc.php blocked (common attack vector)wp-config.php and dotfiles blocked from web accessDISALLOW_FILE_EDIT enabled (prevents theme/plugin editor exploits)WP_AUTO_UPDATE_CORE enabledFORCE_SSL_ADMIN enabledexpose_php = Offdisplay_errors = Offallow_url_include = Offopen_basedir restricted to /var/www/sites/ and /tmp/exec, passthru, shell_exec, system, proc_open, popenhttponly, secure, strict_modelocal_infile = 0 (prevents file read attacks)skip_symbolic_links = yes| Port | Source | Protocol |
|---|---|---|
| 80 | 0.0.0.0/0 | TCP |
| 443 | 0.0.0.0/0 | TCP |
| 22 | Your IP only | TCP |
Block everything else.
Enabled by default in openlitespeed/lsphp.ini:
Install the free LiteSpeed Cache plugin. It’s purpose-built for OpenLiteSpeed and provides page caching, image optimization, and CSS/JS minification.
Redis runs in the stack. Install Redis Object Cache and enable it. Connection settings:
redis6379The mariadb/production.cnf file is pre-tuned for 1-4GB RAM instances:
For larger instances, increase innodb_buffer_pool_size to ~50% of available RAM.
./scripts/backup.sh
./scripts/backup.sh my-s3-bucket-name
0 3 * * * cd /path/to/LAMP-env && ./scripts/backup.sh my-bucket
Backups include all databases, site files, and Caddy configs. Local backups older than 7 days are auto-deleted.
| File | Purpose |
|---|---|
docker-compose.prod.yml |
Service definitions and networking |
openlitespeed/httpd_config.conf |
OLS server config (listeners, LSAPI, modules) |
openlitespeed/vhconf.conf |
WordPress virtual host (rewrites, security, PHP overrides) |
openlitespeed/lsphp.ini |
PHP settings (limits, security, OPcache) |
caddy/Caddyfile |
Main Caddy config (imports site configs) |
caddy/sites/*.caddy |
Per-site reverse proxy configs |
mariadb/production.cnf |
MariaDB tuning |