Multi-Domain Hosting on a Linux VPS: Nginx Blocks & PHP-FPM Pools
Complete tutorial on configuring multiple isolated websites on a single Linux VPS using Nginx server blocks and dedicated user-level PHP-FPM pools.
Hosting multiple websites on a single Linux VPS is a cost-effective strategy for web development agencies and businesses managing multiple web properties. However, running multiple domains under a single shared web server user introduces severe security vulnerabilities and performance bottlenecks. Implementing isolated Nginx server blocks and dedicated PHP-FPM pools ensures complete security sandboxing and resource control across all hosted domains.
Security Risks of Default Shared-User Hosting
By default, standard LEMP (Linux, Nginx, MySQL, PHP) installations execute all PHP scripts under a single shared system user (typically www-data or nginx). In this flat configuration, every website shares identical file read and write permissions.
If an attacker exploits an unpatched WordPress plugin on one secondary website, they instantly gain file read access to every other site sharing the www-data user. They can read configuration files (such as wp-config.php or .env), extract database credentials, and compromise all hosted domains. Creating separate system users and dedicated PHP-FPM execution pools isolates each website into an independent security container.
Step 1: Creating Isolated System Users and Document Roots
For each website, create a dedicated system user without an interactive login shell. This ensures that even if a PHP script is compromised, the attacker cannot execute shell logins or traverse outside the assigned document root.
# Create dedicated system users
sudo useradd -r -s /bin/false site1_user
sudo useradd -r -s /bin/false site2_user
# Create separate web root directories
sudo mkdir -p /var/www/site1.com/public
sudo mkdir -p /var/www/site2.com/public
# Assign strict ownership and permissions
sudo chown -R site1_user:site1_user /var/www/site1.com
sudo chown -R site2_user:site2_user /var/www/site2.com
sudo chmod 750 /var/www/site1.com
sudo chmod 750 /var/www/site2.comAdd the Nginx runtime user (www-data) to each site group so Nginx can read static assets (images, CSS, JS) directly:
sudo usermod -aG site1_user www-data
sudo usermod -aG site2_user www-dataStep 2: Configuring Dedicated PHP-FPM Pools
PHP-FPM allows you to create independent worker pools. Each pool listens on its own distinct Unix domain socket and runs under its designated system user.
Create the pool configuration file for the first domain at /etc/php/8.2/fpm/pool.d/site1.conf:
[site1]
user = site1_user
group = site1_user
listen = /run/php/php8.2-fpm-site1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500
chdir = /var/www/site1.com/public
php_admin_value[open_basedir] = /var/www/site1.com/public:/tmpNotice the open_basedir directive: it restricts PHP file access strictly to the website's document root and the system temporary directory. Create a matching configuration for site2.conf with site2_user and its respective socket path.
Test the PHP-FPM configuration syntax and restart the service:
sudo php-fpm8.2 -t
sudo systemctl restart php8.2-fpmStep 3: Configuring Nginx Server Blocks
Next, configure Nginx to route domain traffic to its corresponding document root and isolated PHP-FPM Unix socket.
Create the configuration for the first site at /etc/nginx/sites-available/site1.com.conf:
server {
listen 80;
listen [::]:80;
server_name site1.com www.site1.com;
root /var/www/site1.com/public;
index index.php index.html;
access_log /var/log/nginx/site1.access.log;
error_log /var/log/nginx/site1.error.log;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm-site1.sock;
}
location ~ /\.ht {
deny all;
}
}Enable the site by symlinking to sites-enabled, test the syntax, and reload Nginx:
sudo ln -s /etc/nginx/sites-available/site1.com.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxStep 4: Automated SSL Certificates and Validation
Automate SSL provisioning across all hosted domains using Certbot and the Nginx plugin:
sudo certbot --nginx -d site1.com -d www.site1.com
sudo certbot --nginx -d site2.com -d www.site2.comCertbot automatically updates the Nginx server blocks with HTTPS listen directives, SSL certificate paths, and HTTP-to-HTTPS redirect rules.
Step 5: Log Rotation and Subdomain Management per Isolated User
When running multiple websites, access and error logs can rapidly consume NVMe disk space if left unmanaged. Configure logrotate to automatically compress and retain logs on a weekly schedule.
Create /etc/logrotate.d/nginx-multi-domain:
/var/log/nginx/*.access.log /var/log/nginx/*.error.log {
weekly
missingok
rotate 12
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}For staging subdomains (e.g., staging.site1.com), create a subfolder under /var/www/site1.com/staging owned by the same site1_user, allowing safe pre-production testing within the exact same security boundaries.
Multi-Domain VPS Resource Sizing Matrix
| Hosted Websites | Traffic Tier | Recommended VPS Plan | Resource Allocation Strategy |
|---|---|---|---|
| 2–3 Brochure Websites | Low (< 10k visits/mo each) | VPS 2: 2 vCPU, 2 GB RAM | pm.max_children = 5 per pool, OPcache enabled |
| 5–8 Dynamic WordPress Sites | Medium (10k–50k visits/mo each) | VPS 8: 4 vCPU, 8 GB RAM | pm.max_children = 10 per pool, Redis object cache |
| 10+ High-Traffic Domains / WooCommerce | Heavy (100k+ visits/mo) | 9950X High-Frequency VPS (16–32 GB RAM) | pm.max_children = 25+, FastCGI microcaching |
VPSWala provides high-performance KVM Cloud VPS hosting built on enterprise RAID NVMe arrays with complete root access, allowing developers to configure custom multi-domain architectures without artificial hosting restrictions. Explore our Cloud VPS Plans or evaluate high-frequency single-thread power with our AMD Ryzen 9 9950X VPS.
Sources
Not sure which size?
Send the stack, get a size.
Tell us the operating system, application stack, current traffic, database size and where it hurts today. You get a sizing recommendation, the matching plan and a price.
Related
Laravel VPS Deployment Guide: Nginx Configuration, PHP-FPM Pools & OPcache Tuning
A production deployment runbook for Laravel on Linux KVM VPS, covering Nginx server blocks, PHP-FPM process pools, OPcache tuning, and queue management.
MongoDB VPS Requirements: WiredTiger Cache Sizing, Memory & NVMe Storage
A technical database sizing guide for MongoDB on Linux KVM VPS, covering WiredTiger cache calculations, memory allocation, and swap tuning.
VPS Hosting for Varanasi: Which VPSWala Node to Pick and How to Test It
A technical node selection and ICMP diagnostic guide for Varanasi developers and businesses deploying KVM Cloud VPS instances across India.