Skip to the content.

Multi-Domain Setup

Run multiple websites on a single VPS with isolated environments.

Overview

svp supports hosting multiple domains on one server. Each domain gets:


Basic Multi-Domain Setup

Using Extra Domains Flag

Provision multiple domains in one command:

sudo svp setup example.com \
  --cms drupal \
  --extra-domains "staging.example.com,dev.example.com" \
  --le-email admin@example.com

This creates:

/var/www/
├── example.com/          # Production
├── staging.example.com/  # Staging
└── dev.example.com/      # Development

Each with:


Multiple Separate Setups

Add Domains One at a Time

Run svp multiple times for different domains:

# First site
sudo svp setup site1.com --cms drupal --le-email admin@site1.com

# Second site
sudo svp setup site2.com --cms drupal --le-email admin@site2.com

# Third site
sudo svp setup blog.com --cms wordpress --le-email admin@blog.com

Advantage: Each site can have:


Environment-Based Setup

Production + Staging + Development

Perfect for development workflow:

sudo svp setup myapp.com \
  --cms drupal \
  --extra-domains "staging.myapp.com,dev.myapp.com" \
  --git-repo https://github.com/company/myapp.git \
  --le-email devops@company.com

# Password-protect non-production environments
sudo svp auth staging.myapp.com enable --username team --password stagingPass
sudo svp auth dev.myapp.com enable --username dev --password devPass

Workflow:

  1. Development (dev.myapp.com)
    • Feature branch testing
    • Rapid iteration
    • Latest code
    • Password-protected from public
  2. Staging (staging.myapp.com)
    • Pre-production testing
    • QA environment
    • Password-protected for client previews
    • Mirror of production data
  3. Production (myapp.com)
    • Live site
    • Stable branch only
    • Production data

Switch branches per environment:

# Development - feature branch
cd /var/www/dev.myapp.com
sudo -u admin git checkout feature/new-feature

# Staging - develop branch
cd /var/www/staging.myapp.com
sudo -u admin git checkout develop

# Production - main branch
cd /var/www/myapp.com
sudo -u admin git checkout main

Resource Allocation

PHP-FPM Pool Isolation

Each domain has isolated PHP processes:

# View running pools
sudo systemctl status php8.3-fpm

# Check pool configs
ls -l /etc/php/8.3/fpm/pool.d/

Per-domain pool configuration:

/etc/php/8.3/fpm/pool.d/
├── example.com.conf
├── staging.example.com.conf
└── dev.example.com.conf

Database Isolation

Each domain has its own database:

# List databases
sudo mysql -e "SHOW DATABASES LIKE '%example%';"

Output:

drupal_example_com
drupal_staging_example_com
drupal_dev_example_com

Process Isolation Benefits

  1. Security - One site compromise doesn’t affect others
  2. Performance - Limit resources per site
  3. Debugging - Easy to identify which site has issues
  4. Flexibility - Different PHP versions per site

Managing Multiple Sites

Drush Aliases

Each Drupal site gets its own drush command:

# Production
drush-example.com status

# Staging
drush-staging.example.com status

# Development
drush-dev.example.com status

WP-CLI for WordPress

# Site 1
cd /var/www/site1.com
sudo -u admin wp plugin list

# Site 2
cd /var/www/site2.com
sudo -u admin wp theme list

Database Credentials

Each site has separate credentials:

# Production
sudo cat /etc/svp/sites/example.com.db.txt

# Staging
sudo cat /etc/svp/sites/staging.example.com.db.txt

# Development
sudo cat /etc/svp/sites/dev.example.com.db.txt

SSL for Multiple Domains

Automatic SSL

Each domain gets its own certificate:

# View all certificates
sudo certbot certificates

Output:

Certificate Name: example.com
  Domains: example.com
  
Certificate Name: staging.example.com
  Domains: staging.example.com
  
Certificate Name: dev.example.com
  Domains: dev.example.com

Renewal

All certificates auto-renew via certbot timer:

sudo systemctl status certbot.timer

DNS Configuration

Setup DNS for Multiple Domains

Each domain needs an A record pointing to your server:

A    example.com             123.45.67.89
A    staging.example.com     123.45.67.89
A    dev.example.com         123.45.67.89
A    site1.com               123.45.67.89
A    site2.com               123.45.67.89

For subdomains:

A    staging                 123.45.67.89
A    dev                     123.45.67.89

Or use wildcard (if all subdomains point to same server):

A    *                       123.45.67.89

Server Resources

Calculating Capacity

PHP-FPM Memory:

Total estimate for 3 sites:

Recommendation:

Monitor Resources

# Overall memory
free -h

# Per-process memory
ps aux --sort=-%mem | head -20

# PHP-FPM pool processes
ps aux | grep php-fpm

Nginx Configuration

Virtual Hosts

Each domain gets its own Nginx config:

ls -l /etc/nginx/sites-available/

Output:

example.com.conf
staging.example.com.conf
dev.example.com.conf

Check Configuration

# Test all configs
sudo nginx -t

# Reload after changes
sudo systemctl reload nginx

Different PHP Versions

Run Different PHP per Domain

# Site 1 with PHP 8.3
sudo svp setup site1.com --cms drupal --php-version 8.3

# Site 2 with PHP 8.4
sudo svp setup site2.com --cms drupal --php-version 8.4

Each site uses its specified PHP-FPM version independently.


Data Synchronization

Sync Staging from Production

Copy database and files from production to staging:

1. Export production database:

drush-example.com sql-dump --gzip > prod-db.sql.gz

2. Import to staging:

zcat prod-db.sql.gz | drush-staging.example.com sql-cli

3. Sync files:

rsync -avz --delete \
  /var/www/example.com/web/sites/default/files/ \
  /var/www/staging.example.com/web/sites/default/files/

4. Clear cache:

drush-staging.example.com cr

Automated Sync Script

Create /usr/local/bin/sync-to-staging.sh:

#!/bin/bash
set -e

PROD="/var/www/example.com"
STAGING="/var/www/staging.example.com"

echo "Syncing production to staging..."

# Database
echo "Exporting production database..."
drush-example.com sql-dump --gzip > /tmp/prod-sync.sql.gz

echo "Importing to staging..."
zcat /tmp/prod-sync.sql.gz | drush-staging.example.com sql-cli
rm /tmp/prod-sync.sql.gz

# Files
echo "Syncing files..."
rsync -avz --delete \
  $PROD/web/sites/default/files/ \
  $STAGING/web/sites/default/files/

# Clear cache
echo "Clearing staging cache..."
drush-staging.example.com cr

echo "Sync complete!"

Make executable:

sudo chmod +x /usr/local/bin/sync-to-staging.sh

Run:

sudo /usr/local/bin/sync-to-staging.sh

Removing Sites

Delete a Domain

To remove a site completely:

# 1. Remove SSL certificate
sudo certbot delete --cert-name staging.example.com

# 2. Remove Nginx config
sudo rm /etc/nginx/sites-enabled/staging.example.com.conf
sudo rm /etc/nginx/sites-available/staging.example.com.conf
sudo systemctl reload nginx

# 3. Stop and remove PHP-FPM pool
sudo rm /etc/php/8.3/fpm/pool.d/staging.example.com.conf
sudo systemctl restart php8.3-fpm

# 4. Drop database
sudo mysql <<EOF
DROP DATABASE IF EXISTS drupal_staging_example_com;
DROP USER IF EXISTS 'drupal_staging_example_com'@'localhost';
EOF

# 5. Remove site directory
sudo rm -rf /var/www/staging.example.com

# 6. Remove site config
sudo rm /etc/svp/sites/staging.example.com.*

Best Practices

1. Use Consistent Naming

example.com           # Production
staging.example.com   # Staging
dev.example.com       # Development

2. Separate Git Branches

3. Different Databases

Never share databases between environments. Always use separate databases.

4. Monitor Resources

Keep an eye on server resources:

# Check memory
free -h

# Check disk
df -h

# Check load
uptime

5. Regular Backups

Back up each site independently:

# Production
drush-example.com sql-dump --gzip > prod-backup.sql.gz

# Staging
drush-staging.example.com sql-dump --gzip > staging-backup.sql.gz

Troubleshooting

Wrong Site Loading

Check Nginx configuration:

sudo nginx -t
sudo systemctl reload nginx

Verify server_name in config:

grep server_name /etc/nginx/sites-available/example.com.conf

PHP-FPM Issues

Check pool is running:

sudo systemctl status php8.3-fpm

View pool logs:

sudo tail -f /var/log/php8.3-fpm-example.com-error.log

Database Connection

Verify credentials:

sudo cat /etc/svp/sites/example.com.db.txt

Test connection:

mysql -u drupal_example_com -p

← SSL Configuration Git Deployment →