{"id":23884,"date":"2026-08-02T07:48:09","date_gmt":"2026-08-02T07:48:09","guid":{"rendered":"https:\/\/atalnetworks.com\/?p=23884"},"modified":"2026-08-02T10:19:08","modified_gmt":"2026-08-02T10:19:08","slug":"how-to-host-multiple-websites-on-one-vps","status":"publish","type":"post","link":"https:\/\/atalnetworks.com\/es\/how-to-host-multiple-websites-on-one-vps\/","title":{"rendered":"How to Host Multiple Websites on One VPS | Atal Networks"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">Paying for five separate hosting plans to run five low-traffic sites is money left on the table. One properly configured VPS handles them all, and every site performs better than it would on shared hosting.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Hosting multiple websites on one VPS works by running a web server like Nginx or Apache that reads the domain name in each incoming HTTP request and routes it to the correct site directory. Each site gets its own document root, database, SSL certificate, and PHP process pool. One server, one IP address, as many domains as your RAM and CPU can support.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This guide covers the complete setup: directory structure, Nginx server blocks, Apache virtual hosts, PHP-FPM pool isolation, free SSL with Certbot, security hardening, Redis caching, automated backups, and a clear framework for knowing when a single VPS is no longer enough.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Can You Host Multiple Websites on One VPS?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">A single VPS can host anywhere from 3 to 20+ websites. The web server software handles all traffic routing. Nginx uses server blocks and Apache uses virtual hosts. Both work by reading the <\/span><span style=\"font-weight: 400;\">Host<\/span><span style=\"font-weight: 400;\"> header in each HTTP request and matching it to a configured domain name. The matched domain maps to a specific directory on disk, where the site&#8217;s files live. One IP address, one server, many domains.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This works at the KVM virtualization layer too. Our <\/span><a href=\"https:\/\/atalnetworks.com\/linux-vps-hosting\/\"><span style=\"font-weight: 400;\">Linux VPS hosting<\/span><\/a><span style=\"font-weight: 400;\"> plans run KVM-based isolation, meaning your VPS has a fixed slice of CPU and RAM that no other customer shares. That isolation is what makes multi-site consolidation on a VPS reliable, unlike shared hosting, where hundreds of accounts compete for the same physical resources with no guaranteed allocation.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">How Many Sites Can One VPS Handle?<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">The honest answer depends on traffic volume and whether your sites use caching. Here is a practical reference based on real-world WordPress deployments:<\/span><\/p>\n<table>\n<thead>\n<tr>\n<th><b>VPS Specs<\/b><\/th>\n<th><b>Low-Traffic Sites (&lt;500 visitors\/day)<\/b><\/th>\n<th><b>Medium-Traffic Sites (500\u20135,000\/day)<\/b><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><span style=\"font-weight: 400;\">2 vCPU \/ 2 GB RAM<\/span><\/td>\n<td><span style=\"font-weight: 400;\">3\u20135 sites<\/span><\/td>\n<td><span style=\"font-weight: 400;\">1\u20132 sites<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">2 vCPU \/ 4 GB RAM<\/span><\/td>\n<td><span style=\"font-weight: 400;\">5\u201310 sites<\/span><\/td>\n<td><span style=\"font-weight: 400;\">3\u20135 sites<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">4 vCPU \/ 8 GB RAM<\/span><\/td>\n<td><span style=\"font-weight: 400;\">10\u201320 sites<\/span><\/td>\n<td><span style=\"font-weight: 400;\">5\u201310 sites<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"font-weight: 400;\">The bottleneck is almost always RAM. Use this formula to size PHP-FPM workers:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">max_children = available_RAM_for_PHP \u00f7 average_PHP_process_size_MB<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Average WordPress PHP-FPM process: 30\u201350 MB. On a 4 GB VPS, reserve 1 GB for the OS and MariaDB. That leaves 3 GB for PHP, which supports roughly 60\u2013100 total workers across all sites. With Redis object caching enabled, these numbers are conservative because Redis absorbs most database reads before PHP ever sees them.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Shared Hosting vs. VPS for Multiple Sites<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Shared hosting puts hundreds of tenants on one machine with no resource guarantees. When one site spikes in traffic, every other site on that machine slows down. A VPS gives you a dedicated slice of CPU and RAM. PHP-FPM pools keep each site&#8217;s processes isolated so a traffic spike on site1.com cannot starve site2.com of workers.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Before You Start: Prerequisites<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Get these in place before touching any configuration files:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">A VPS running Ubuntu 22.04 or 24.04 with root or sudo access<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">At least 2 GB RAM (4 GB recommended for 5+ sites)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Domain names registered with DNS A records pointing to your VPS public IP<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">SSH key authentication configured and password authentication disabled<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Basic Linux command line familiarity (file editing, service management)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">UFW firewall installed<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">DNS A records push out across global resolvers within 24\u201348 hours. Test resolution with <\/span><span style=\"font-weight: 400;\">dig site1.com<\/span><span style=\"font-weight: 400;\"> before running web server configuration. If the command returns your VPS IP, the domain is ready.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Set Up Your Directory Structure First<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Most guides jump straight to web server configuration. Setting up a clean directory structure first saves hours of debugging later. Permissions, backup scripts, and log rotation all depend on a consistent layout.<\/span><\/p>\n<p><img fetchpriority=\"high\" decoding=\"async\" class=\"alignnone size-full wp-image-23891\" src=\"https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Set-Up-Your-Directory-Structure-First.webp\" alt=\"Set Up Your Directory Structure First\" width=\"1672\" height=\"941\" srcset=\"https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Set-Up-Your-Directory-Structure-First.webp 1672w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Set-Up-Your-Directory-Structure-First-300x169.webp 300w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Set-Up-Your-Directory-Structure-First-1024x576.webp 1024w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Set-Up-Your-Directory-Structure-First-768x432.webp 768w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Set-Up-Your-Directory-Structure-First-1536x864.webp 1536w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Set-Up-Your-Directory-Structure-First-18x10.webp 18w\" sizes=\"(max-width: 1672px) 100vw, 1672px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">Use this structure for every site:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\/var\/www\/<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u251c\u2500\u2500 site1.com\/<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u2502 \u00a0 \u251c\u2500\u2500 public\/\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2190 document root (web-accessible files)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u2502 \u00a0 \u2514\u2500\u2500 logs\/\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2190 per-site access and error logs<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u251c\u2500\u2500 site2.com\/<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u2502 \u00a0 \u251c\u2500\u2500 public\/<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u2502 \u00a0 \u2514\u2500\u2500 logs\/<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Create the directories:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo mkdir -p \/var\/www\/site1.com\/{public,logs}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo mkdir -p \/var\/www\/site2.com\/{public,logs}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo chown -R www-data:www-data \/var\/www\/site1.com\/public<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo chown -R www-data:www-data \/var\/www\/site2.com\/public<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo chmod -R 755 \/var\/www<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Separate log directories per site mean you can run <\/span><span style=\"font-weight: 400;\">tail -f \/var\/www\/site1.com\/logs\/error.log<\/span><span style=\"font-weight: 400;\"> to debug one site without seeing noise from all the others.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Create a Separate Linux User Per Site<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Running every site under <\/span><span style=\"font-weight: 400;\">www-data<\/span><span style=\"font-weight: 400;\"> is the default and the wrong choice for multi-site security. If one site gets compromised, a process running as <\/span><span style=\"font-weight: 400;\">www-data<\/span><span style=\"font-weight: 400;\"> can read every other site&#8217;s files and database credentials.<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">A separate system user per site stops lateral movement after a compromise:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo adduser &#8211;no-create-home &#8211;shell \/usr\/sbin\/nologin site1user<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo adduser &#8211;no-create-home &#8211;shell \/usr\/sbin\/nologin site2user<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo chown -R site1user:www-data \/var\/www\/site1.com\/public<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo chown -R site2user:www-data \/var\/www\/site2.com\/public<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Each site&#8217;s PHP-FPM pool will run under its matching system user. A malicious script on site1.com runs as <\/span><span style=\"font-weight: 400;\">site1user<\/span><span style=\"font-weight: 400;\">, which has no read access to <\/span><span style=\"font-weight: 400;\">\/var\/www\/site2.com\/<\/span><span style=\"font-weight: 400;\">.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Install the Web Stack (Nginx + PHP-FPM + MariaDB)<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Update the system and install the full stack on Ubuntu 24.04:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo apt update &amp;&amp; sudo apt upgrade -y<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo apt install nginx mariadb-server php8.3-fpm \\<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0php8.3-mysql php8.3-curl php8.3-gd \\<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0php8.3-mbstring php8.3-xml php8.3-zip -y<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo mysql_secure_installation<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Answer yes to all <\/span><span style=\"font-weight: 400;\">mysql_secure_installation<\/span><span style=\"font-weight: 400;\"> prompts: set a root password, remove anonymous users, disallow remote root login, remove the test database, and reload privileges.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Nginx uses an event-driven, asynchronous architecture that handles thousands of concurrent connections with minimal memory. Apache&#8217;s process-based model uses more RAM per connection but remains the better choice for applications that rely on <\/span><span style=\"font-weight: 400;\">.htaccess<\/span><span style=\"font-weight: 400;\"> files or per-directory configuration without server-level config access. For most modern PHP and WordPress deployments, Nginx is the right default.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Configure Nginx Server Blocks for Multiple Domains<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Nginx reads the <\/span><span style=\"font-weight: 400;\">Host<\/span><span style=\"font-weight: 400;\"> header on every incoming request and matches it against <\/span><span style=\"font-weight: 400;\">server_name<\/span><span style=\"font-weight: 400;\"> directives in your configuration files. The matching block determines which document root serves the response. Multiple blocks share ports 80 and 443, and Nginx handles all routing internally.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Store each site&#8217;s configuration in <\/span><span style=\"font-weight: 400;\">\/etc\/nginx\/sites-available\/<\/span><span style=\"font-weight: 400;\">, then create a symlink to <\/span><span style=\"font-weight: 400;\">\/etc\/nginx\/sites-enabled\/<\/span><span style=\"font-weight: 400;\"> to activate it. This separation lets you disable a site without deleting its config.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Create a Server Block for Each Domain<\/span><\/h3>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-23892\" src=\"https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Create-a-Server-Block-for-Each-Domain.webp\" alt=\"Create a Server Block for Each Domain\" width=\"1672\" height=\"941\" srcset=\"https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Create-a-Server-Block-for-Each-Domain.webp 1672w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Create-a-Server-Block-for-Each-Domain-300x169.webp 300w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Create-a-Server-Block-for-Each-Domain-1024x576.webp 1024w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Create-a-Server-Block-for-Each-Domain-768x432.webp 768w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Create-a-Server-Block-for-Each-Domain-1536x864.webp 1536w, https:\/\/atalnetworks.com\/wp-content\/uploads\/2025\/04\/Create-a-Server-Block-for-Each-Domain-18x10.webp 18w\" sizes=\"(max-width: 1672px) 100vw, 1672px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">Full production-ready configuration for site1.com. Create the file:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo nano \/etc\/nginx\/sites-available\/site1.com<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Paste this configuration:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">server {<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0listen 80;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0listen [::]:80;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0server_name site1.com www.site1.com;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0root \/var\/www\/site1.com\/public;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0index index.php index.html;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0access_log \/var\/www\/site1.com\/logs\/access.log;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0error_log\u00a0 \/var\/www\/site1.com\/logs\/error.log;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0client_max_body_size 64M;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0# Security headers<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0add_header X-Frame-Options\u00a0 \u00a0 \u00a0 \u00a0 &#8220;SAMEORIGIN&#8221;\u00a0 \u00a0 always;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0add_header X-Content-Type-Options &#8220;nosniff&#8221; \u00a0 \u00a0 \u00a0 always;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0add_header X-XSS-Protection \u00a0 \u00a0 \u00a0 &#8220;1; mode=block&#8221; always;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0location \/ {<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0try_files $uri $uri\/ \/index.php?$args;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0location ~ \\.php$ {<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0include snippets\/fastcgi-php.conf;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0fastcgi_pass unix:\/run\/php\/php8.3-fpm-site1.sock;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0# Block access to sensitive files<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0location ~ \/\\.ht \u00a0 \u00a0 \u00a0 \u00a0 { deny all; }<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0location = \/wp-config.php { deny all; }<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0# Cache static assets at the browser level<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0location ~* \\.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0expires 30d;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0add_header Cache-Control &#8220;public, immutable&#8221;;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Repeat the same process for site2.com, changing <\/span><span style=\"font-weight: 400;\">server_name<\/span><span style=\"font-weight: 400;\">, <\/span><span style=\"font-weight: 400;\">root<\/span><span style=\"font-weight: 400;\">, <\/span><span style=\"font-weight: 400;\">access_log<\/span><span style=\"font-weight: 400;\">, <\/span><span style=\"font-weight: 400;\">error_log<\/span><span style=\"font-weight: 400;\">, and the <\/span><span style=\"font-weight: 400;\">fastcgi_pass<\/span><span style=\"font-weight: 400;\"> socket path.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Enable both sites, remove the default, and test:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo ln -s \/etc\/nginx\/sites-available\/site1.com \/etc\/nginx\/sites-enabled\/<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo ln -s \/etc\/nginx\/sites-available\/site2.com \/etc\/nginx\/sites-enabled\/<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo rm \/etc\/nginx\/sites-enabled\/default<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo nginx -t<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo systemctl reload nginx<\/span><\/p>\n<p><span style=\"font-weight: 400;\">A clean <\/span><span style=\"font-weight: 400;\">nginx -t<\/span><span style=\"font-weight: 400;\"> output confirms no syntax errors before you reload.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Add a Catch-All Server Block (The Security Step Most Guides Skip)<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Without a catch-all default block, Nginx serves the first alphabetically-loaded config file to any request that does not match a known <\/span><span style=\"font-weight: 400;\">server_name<\/span><span style=\"font-weight: 400;\">. Automated scanners and bots probe VPS IP addresses constantly. Serving your site content to an unknown host header leaks information and can expose application details.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Add this block:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo nano \/etc\/nginx\/sites-available\/000-catch-all<\/span><\/p>\n<p><span style=\"font-weight: 400;\">server {<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0listen 80\u00a0 default_server;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0listen [::]:80 default_server;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0server_name _;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0return 444;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo ln -s \/etc\/nginx\/sites-available\/000-catch-all \/etc\/nginx\/sites-enabled\/<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo nginx -t &amp;&amp; sudo systemctl reload nginx<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Return code <\/span><span style=\"font-weight: 400;\">444<\/span><span style=\"font-weight: 400;\"> closes the TCP connection immediately with no HTTP response. Scanners get nothing. The <\/span><span style=\"font-weight: 400;\">000-<\/span><span style=\"font-weight: 400;\"> prefix in the filename ensures this block loads before your site configs alphabetically, making it the true default.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Apache Virtual Hosts (For Legacy or .htaccess-Dependent Apps)<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">If your application requires <\/span><span style=\"font-weight: 400;\">.htaccess<\/span><span style=\"font-weight: 400;\"> support or you are running legacy PHP software that expects Apache-style per-directory configuration, use Apache virtual hosts instead of Nginx server blocks.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Create the configuration file:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo nano \/etc\/apache2\/sites-available\/site1.com.conf<\/span><\/p>\n<p><span style=\"font-weight: 400;\">&lt;VirtualHost *:80&gt;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0ServerName\u00a0 \u00a0 site1.com<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0ServerAlias \u00a0 www.site1.com<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0DocumentRoot\u00a0 \/var\/www\/site1.com\/public<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0ErrorLog\u00a0 \u00a0 \u00a0 \/var\/www\/site1.com\/logs\/error.log<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0CustomLog \u00a0 \u00a0 \/var\/www\/site1.com\/logs\/access.log combined<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&lt;Directory \/var\/www\/site1.com\/public&gt;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Options -Indexes +FollowSymLinks<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0AllowOverride All<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Require all granted<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&lt;\/Directory&gt;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">&lt;\/VirtualHost&gt;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Enable the site and reload:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo a2ensite site1.com.conf<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo systemctl reload apache2<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Repeat for each additional domain.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Configure Separate PHP-FPM Pools Per Site<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">The default <\/span><span style=\"font-weight: 400;\">www.conf<\/span><span style=\"font-weight: 400;\"> PHP-FPM pool runs all PHP requests under <\/span><span style=\"font-weight: 400;\">www-data<\/span><span style=\"font-weight: 400;\"> with a shared process pool. A traffic spike on site1.com or a runaway WordPress cron job can exhaust all available PHP workers and crash every other site on the server. Separate pools prevent this entirely.<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Create a dedicated pool config for site1:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo nano \/etc\/php\/8.3\/fpm\/pool.d\/site1.conf<\/span><\/p>\n<p><span style=\"font-weight: 400;\">[site1]<\/span><\/p>\n<p><span style=\"font-weight: 400;\">user\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 = site1user<\/span><\/p>\n<p><span style=\"font-weight: 400;\">group \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 = www-data<\/span><\/p>\n<p><span style=\"font-weight: 400;\">listen\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 = \/run\/php\/php8.3-fpm-site1.sock<\/span><\/p>\n<p><span style=\"font-weight: 400;\">listen.owner\u00a0 \u00a0 \u00a0 \u00a0 = www-data<\/span><\/p>\n<p><span style=\"font-weight: 400;\">listen.group\u00a0 \u00a0 \u00a0 \u00a0 = www-data<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pm\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 = dynamic<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pm.max_children \u00a0 \u00a0 = 8<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pm.start_servers\u00a0 \u00a0 = 2<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pm.min_spare_servers = 1<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pm.max_spare_servers = 3<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pm.max_requests \u00a0 \u00a0 = 500<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Duplicate this file for each site, incrementing the pool name, user, and socket path. Disable the default <\/span><span style=\"font-weight: 400;\">www.conf<\/span><span style=\"font-weight: 400;\"> pool to prevent it from consuming resources:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo mv \/etc\/php\/8.3\/fpm\/pool.d\/www.conf \/etc\/php\/8.3\/fpm\/pool.d\/www.conf.disabled<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo systemctl restart php8.3-fpm<\/span><\/p>\n<p><b>Sizing guide:<\/b><span style=\"font-weight: 400;\"> On a 4 GB VPS, reserve 1 GB for the OS and MariaDB. 3 GB remains for PHP. At 40 MB average per WordPress PHP-FPM process, you have room for roughly 75 total workers. Across 10 sites, set <\/span><span style=\"font-weight: 400;\">pm.max_children = 7<\/span><span style=\"font-weight: 400;\"> or <\/span><span style=\"font-weight: 400;\">8<\/span><span style=\"font-weight: 400;\"> per site. Adjust based on each site&#8217;s traffic profile: a high-traffic site gets more workers, a low-traffic staging site gets fewer.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Verify the pools are running after restart:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo systemctl status php8.3-fpm<\/span><\/p>\n<p><span style=\"font-weight: 400;\">ls \/run\/php\/<\/span><\/p>\n<p><span style=\"font-weight: 400;\">You should see one <\/span><span style=\"font-weight: 400;\">.sock<\/span><span style=\"font-weight: 400;\"> file per configured pool.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Install SSL Certificates for All Domains<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Every site on your VPS needs HTTPS. Let&#8217;s Encrypt provides free, automatically renewing SSL certificates through Certbot. Certbot covers all domains in a single command.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Install Certbot:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo apt install certbot python3-certbot-nginx -y<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Generate certificates for all domains at once:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo certbot &#8211;nginx \\<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0-d site1.com -d www.site1.com \\<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0-d site2.com -d www.site2.com<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Certbot modifies your Nginx server blocks automatically: it adds port 443 listeners, SSL certificate file paths, and HTTP-to-HTTPS redirect rules. After it runs, Nginx serves every site over HTTPS without any manual configuration changes.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Verify that auto-renewal works before trusting it:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo certbot renew &#8211;dry-run<\/span><\/p>\n<p><span style=\"font-weight: 400;\">A successful dry run confirms Certbot can reach Let&#8217;s Encrypt&#8217;s servers and renew certificates without human input. Certbot installs a systemd timer by default on Ubuntu. Check its status:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo systemctl status certbot.timer<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">SSL with Multiple Domains on One IP: SNI Explained<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Running multiple SSL certificates on one IP address works through Server Name Indication (SNI). During the TLS handshake, the client&#8217;s browser sends the requested hostname before the HTTP connection is established. Nginx reads that hostname and serves the matching certificate. Every browser released after 2010 supports SNI. You do not need a separate IP address per domain, and adding IP addresses solely for SSL is unnecessary cost.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Harden Security Across All Sites<\/span><\/h2>\n<h3><span style=\"font-weight: 400;\">Enable UFW Firewall<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">A firewall limits which ports accept connections. Only open what your server actually needs:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo ufw allow 22\/tcp\u00a0 \u00a0 # SSH<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo ufw allow 80\/tcp\u00a0 \u00a0 # HTTP<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo ufw allow 443\/tcp \u00a0 # HTTPS<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo ufw enable<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo ufw status<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Everything else is blocked by default. If you run other services (mail, custom ports), add specific rules rather than opening broad ranges.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Install Fail2Ban<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Fail2Ban monitors log files for repeated failed login attempts and bans the offending IP address automatically. It protects SSH by default and can be extended to WordPress login pages and other web endpoints.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo apt install fail2ban -y<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo systemctl enable fail2ban<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Create a local jail configuration:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo nano \/etc\/fail2ban\/jail.local<\/span><\/p>\n<p><span style=\"font-weight: 400;\">[DEFAULT]<\/span><\/p>\n<p><span style=\"font-weight: 400;\">bantime\u00a0 = 3600<\/span><\/p>\n<p><span style=\"font-weight: 400;\">findtime = 600<\/span><\/p>\n<p><span style=\"font-weight: 400;\">maxretry = 3<\/span><\/p>\n<p><span style=\"font-weight: 400;\">[sshd]<\/span><\/p>\n<p><span style=\"font-weight: 400;\">enabled = true<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo systemctl restart fail2ban<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo fail2ban-client status sshd<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Set Correct File Permissions<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">The right permissions per resource type:<\/span><\/p>\n<table>\n<thead>\n<tr>\n<th><b>Resource<\/b><\/th>\n<th><b>Permission<\/b><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><span style=\"font-weight: 400;\">Directories<\/span><\/td>\n<td><span style=\"font-weight: 400;\">755<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">PHP and HTML files<\/span><\/td>\n<td><span style=\"font-weight: 400;\">644<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">wp-config.php<\/span><\/td>\n<td><span style=\"font-weight: 400;\">600<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">Upload directories<\/span><\/td>\n<td><span style=\"font-weight: 400;\">755<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"font-weight: 400;\">With each site running under its own system user, <\/span><span style=\"font-weight: 400;\">site2user<\/span><span style=\"font-weight: 400;\"> cannot read files owned by <\/span><span style=\"font-weight: 400;\">site1user<\/span><span style=\"font-weight: 400;\"> regardless of permissions. That separation is the structural protection. Permissions add a second layer.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Disable Nginx Version Disclosure<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">By default, Nginx includes its version number in HTTP response headers and error pages. Attackers use this to target known vulnerabilities in specific versions. Turn it off in <\/span><span style=\"font-weight: 400;\">\/etc\/nginx\/nginx.conf<\/span><span style=\"font-weight: 400;\"> inside the <\/span><span style=\"font-weight: 400;\">http {}<\/span><span style=\"font-weight: 400;\"> block:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">server_tokens off;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo nginx -t &amp;&amp; sudo systemctl reload nginx<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Add Redis Object Caching for WordPress Sites<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Redis stores frequently queried database results in memory. For WordPress sites sharing a MariaDB instance, Redis can reduce database query load by 60\u201380% on read-heavy pages. That reduction directly increases how many concurrent visitors each site handles before hitting resource limits.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Install Redis:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo apt install redis-server -y<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo systemctl enable redis-server<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo systemctl start redis-server<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The critical multi-site configuration detail that most guides miss: assign each site its own Redis database index. Redis provides 16 database indices (0\u201315) by default. Without different indices, cache keys from site1.com and site2.com can collide and serve the wrong cached content.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In each site&#8217;s <\/span><span style=\"font-weight: 400;\">wp-config.php<\/span><span style=\"font-weight: 400;\">, add the Redis database assignment before the <\/span><span style=\"font-weight: 400;\">\/* That&#8217;s all, stop editing! *\/<\/span><span style=\"font-weight: 400;\"> comment:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\/\/ site1.com wp-config.php<\/span><\/p>\n<p><span style=\"font-weight: 400;\">define( &#8216;WP_REDIS_DATABASE&#8217;, 0 );<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\/\/ site2.com wp-config.php<\/span><\/p>\n<p><span style=\"font-weight: 400;\">define( &#8216;WP_REDIS_DATABASE&#8217;, 1 );<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\/\/ site3.com wp-config.php<\/span><\/p>\n<p><span style=\"font-weight: 400;\">define( &#8216;WP_REDIS_DATABASE&#8217;, 2 );<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Install the &#8220;Redis Object Cache&#8221; plugin by Till Kruss in each WordPress site. Activate it and confirm the connection status shows &#8220;Connected&#8221; in the plugin settings. If you run more than 16 WordPress sites on one server, configure a second Redis instance on a different port.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Set Up Automated Backups<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">A backup strategy that is never tested is not a backup strategy. Automated database dumps and file archives cover the data layer. Off-site sync covers the catastrophic failure scenario.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Create the backup script:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo nano \/usr\/local\/bin\/backup-sites.sh<\/span><\/p>\n<p><span style=\"font-weight: 400;\">#!\/bin\/bash<\/span><\/p>\n<p><span style=\"font-weight: 400;\">BACKUP_DIR=&#8221;\/var\/backups\/multi-site&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">DATE=$(date +%Y-%m-%d)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">mkdir -p &#8220;$BACKUP_DIR\/$DATE&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\"># Databases<\/span><\/p>\n<p><span style=\"font-weight: 400;\">for DB in site1_db site2_db; do<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0mysqldump -u root &#8220;$DB&#8221; | gzip &gt; &#8220;$BACKUP_DIR\/$DATE\/${DB}.sql.gz&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">done<\/span><\/p>\n<p><span style=\"font-weight: 400;\"># Site files<\/span><\/p>\n<p><span style=\"font-weight: 400;\">for SITE in site1.com site2.com; do<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0tar -czf &#8220;$BACKUP_DIR\/$DATE\/${SITE}_files.tar.gz&#8221; \\<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0-C &#8220;\/var\/www\/$SITE&#8221; public<\/span><\/p>\n<p><span style=\"font-weight: 400;\">done<\/span><\/p>\n<p><span style=\"font-weight: 400;\"># Remove backups older than 14 days<\/span><\/p>\n<p><span style=\"font-weight: 400;\">find &#8220;$BACKUP_DIR&#8221; -type d -mtime +14 -exec rm -rf {} + 2&gt;\/dev\/null<\/span><\/p>\n<p><span style=\"font-weight: 400;\">echo &#8220;Backup complete: $DATE&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Make it executable and schedule it:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo chmod +x \/usr\/local\/bin\/backup-sites.sh<\/span><\/p>\n<p><span style=\"font-weight: 400;\">echo &#8220;0 3 * * * root \/usr\/local\/bin\/backup-sites.sh&#8221; | sudo tee \/etc\/cron.d\/multi-site-backup<\/span><\/p>\n<p><span style=\"font-weight: 400;\">For off-site storage, sync the backup directory to any S3-compatible object store using <\/span><span style=\"font-weight: 400;\">rclone<\/span><span style=\"font-weight: 400;\">:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo apt install rclone -y<\/span><\/p>\n<p><span style=\"font-weight: 400;\"># Configure rclone with your storage provider credentials<\/span><\/p>\n<p><span style=\"font-weight: 400;\">rclone sync \/var\/backups\/multi-site remote:your-bucket\/vps-backups<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Add the <\/span><span style=\"font-weight: 400;\">rclone sync<\/span><span style=\"font-weight: 400;\"> command to the backup script after the local archive step. Run a manual restore to a test domain every 90 days. A backup you have never restored is an assumption, not a guarantee.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Monitor Server Health Across All Sites<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Monitoring becomes more important as the number of sites on a single VPS grows. One under-resourced site can create subtle performance degradation across all others before any site crashes outright.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Basic Monitoring with htop and vnstat<\/span><\/h3>\n<p><b>htop<\/b><span style=\"font-weight: 400;\"> gives a real-time view of CPU and RAM usage per process. Sort by <\/span><span style=\"font-weight: 400;\">MEM%<\/span><span style=\"font-weight: 400;\"> to identify which PHP-FPM pool is consuming the most memory. Press <\/span><span style=\"font-weight: 400;\">F6<\/span><span style=\"font-weight: 400;\"> to select the sort column.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sudo apt install htop vnstat -y<\/span><\/p>\n<p><b>vnstat<\/b><span style=\"font-weight: 400;\"> tracks bandwidth usage per network interface over time. Run <\/span><span style=\"font-weight: 400;\">vnstat -l<\/span><span style=\"font-weight: 400;\"> for a live view or <\/span><span style=\"font-weight: 400;\">vnstat -d<\/span><span style=\"font-weight: 400;\"> for daily totals. Useful for identifying which site drives outbound traffic spikes before you get a bandwidth overage notice.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Netdata for Live Server Dashboards<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Netdata installs in one command and provides a browser-based dashboard with CPU, RAM, disk I\/O, and Nginx request rate metrics updated every second:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">wget -O \/tmp\/netdata-kickstart.sh https:\/\/get.netdata.cloud\/kickstart.sh<\/span><\/p>\n<p><span style=\"font-weight: 400;\">sh \/tmp\/netdata-kickstart.sh<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Access the dashboard at <\/span><span style=\"font-weight: 400;\">http:\/\/your-vps-ip:19999<\/span><span style=\"font-weight: 400;\">. Set alerts in <\/span><span style=\"font-weight: 400;\">\/etc\/netdata\/health_alarm_notify.conf<\/span><span style=\"font-weight: 400;\"> for RAM usage above 85% and disk usage above 85%. Those thresholds give you time to act before performance degrades or a disk fills completely.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">For larger deployments running 10+ sites, Prometheus with Grafana provides longer retention and more granular per-site metrics through Nginx&#8217;s stub status module and PHP-FPM&#8217;s status endpoint.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">WordPress Multisite vs. Separate WordPress Installs<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Both approaches work on a VPS. The right choice depends on the relationship between your sites.<\/span><\/p>\n<table>\n<thead>\n<tr>\n<th><b>Factor<\/b><\/th>\n<th><b>WordPress Multisite<\/b><\/th>\n<th><b>Separate Installs<\/b><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><span style=\"font-weight: 400;\">Admin overhead<\/span><\/td>\n<td><span style=\"font-weight: 400;\">One dashboard<\/span><\/td>\n<td><span style=\"font-weight: 400;\">One dashboard per site<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">Plugin conflict risk<\/span><\/td>\n<td><span style=\"font-weight: 400;\">High (plugins shared across all sites)<\/span><\/td>\n<td><span style=\"font-weight: 400;\">None (fully isolated)<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">Performance isolation<\/span><\/td>\n<td><span style=\"font-weight: 400;\">None (shared database tables)<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Full (separate DB, PHP-FPM pool)<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">Traffic spike impact<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Affects all sites in the network<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Contained to one site<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">Theme and user management<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Centralized<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Independent<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">Best for<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Related brand network, educational networks<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Independent client sites, agency work<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><b>Use WordPress Multisite<\/b><span style=\"font-weight: 400;\"> when all sites belong to the same organization, share users or administrators, and use the same theme with minor customization per sub-site. A university with department sub-sites is the classic use case.<\/span><\/p>\n<p><b>Use separate WordPress installs<\/b><span style=\"font-weight: 400;\"> when you host sites for different clients, run projects with different traffic profiles, or need to update, troubleshoot, or migrate one site without touching the others. Separate installs give you complete fault isolation: a broken plugin on site3.com does not affect site1.com or site2.com.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">For most agencies and developers running a mix of client sites and personal projects, separate installs with per-site PHP-FPM pools are the more stable and easier-to-maintain setup. The overhead of managing multiple WordPress admin panels is offset by the cleaner isolation.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">When to Stop Consolidating and Move to a Dedicated Server<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">A multi-site VPS works until one of these conditions becomes consistently true:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Any single site exceeds 50,000 monthly unique visitors and shares the server with 5+ other sites<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">RAM usage stays above 85% for more than 2 hours during peak traffic<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">CPU load average exceeds your VPS vCPU count for sustained periods (check with <\/span><span style=\"font-weight: 400;\">uptime<\/span><span style=\"font-weight: 400;\">)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Compliance requirements (PCI-DSS, HIPAA) mandate physical hardware isolation between workloads<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">You are managing 15+ WordPress installs and plugin update cycles have become a meaningful security risk<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Any site needs resources (RAM, CPU, disk throughput) that would deprive others of acceptable performance<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">At that point, move your highest-traffic sites to their own server. Our <\/span><a href=\"https:\/\/atalnetworks.com\/dedicated-servers\/\"><span style=\"font-weight: 400;\">dedicated servers<\/span><\/a><span style=\"font-weight: 400;\"> start at $99\/month with Intel Xeon processors, NVMe SSD storage, and 10Gbps ports, built for high-traffic workloads that have outgrown shared VPS resources. If you need full hardware isolation without management overhead, our <\/span><a href=\"https:\/\/atalnetworks.com\/premium-bare-metal-servers\/\"><span style=\"font-weight: 400;\">bare metal servers<\/span><\/a><span style=\"font-weight: 400;\"> provide dedicated physical hardware across 213+ global data centers.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">For teams that still want the cost efficiency of a VPS but need significantly more headroom, our <\/span><a href=\"https:\/\/atalnetworks.com\/vps\/\"><span style=\"font-weight: 400;\">VPS Pro and VPS Ultimate plans<\/span><\/a><span style=\"font-weight: 400;\"> scale to 4 cores and 16 GB RAM, which comfortably supports 15\u201320 low-traffic sites or 8\u201310 medium-traffic sites with Redis caching in place.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Vertical scaling (adding RAM and CPU to your current VPS) is the fastest path forward when you&#8217;re approaching limits. Horizontal scaling (splitting sites across multiple VPS instances behind a load balancer) makes sense when individual sites outgrow what a single server can handle.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Frequently Asked Questions<\/span><\/h2>\n<p><b>Can you host multiple websites on one VPS?<\/b><span style=\"font-weight: 400;\"> Yes. A single VPS can host 3 to 20+ websites depending on traffic levels and resource allocation. Nginx server blocks or Apache virtual hosts route each domain to its own document root directory. PHP-FPM pools isolate PHP processes per site so no single site can exhaust workers for the others.<\/span><\/p>\n<p><b>How many websites can a 4 GB VPS handle?<\/b><span style=\"font-weight: 400;\"> A 4 GB KVM VPS comfortably runs 5 to 10 low-to-medium traffic WordPress sites with Redis object caching enabled. Reserve 1 GB for the OS and MariaDB, then allocate the remaining 3 GB across PHP-FPM pools at roughly 40 MB per worker. The real limit is RAM, not CPU, for typical content sites.<\/span><\/p>\n<p><b>Is Nginx or Apache better for hosting multiple sites on one VPS?<\/b><span style=\"font-weight: 400;\"> Nginx is the better default for multi-site VPS setups. Its event-driven architecture handles thousands of concurrent connections with less memory per connection than Apache&#8217;s process-based model. Apache is the right choice for applications that depend on <\/span><span style=\"font-weight: 400;\">.htaccess<\/span><span style=\"font-weight: 400;\"> files or per-directory configuration without access to server-level config.<\/span><\/p>\n<p><b>Do I need a separate IP address for each website?<\/b><span style=\"font-weight: 400;\"> No. Server Name Indication (SNI) lets a single IP address serve different SSL\/TLS certificates for multiple domains. The client sends the requested hostname during the TLS handshake, and Nginx selects the correct certificate before serving any content. All modern browsers support SNI. A separate IP per domain is unnecessary for HTTPS.<\/span><\/p>\n<p><b>How do I stop one website from crashing all others on my VPS?<\/b><span style=\"font-weight: 400;\"> Configure a separate PHP-FPM pool for each site with its own <\/span><span style=\"font-weight: 400;\">pm.max_children<\/span><span style=\"font-weight: 400;\"> limit. Each pool runs under a dedicated system user with its own Unix socket. A traffic spike or runaway cron job on one site exhausts only that site&#8217;s allocated workers, leaving the other pools completely unaffected.<\/span><\/p>\n<p><b>Can I host multiple WordPress sites on one VPS?<\/b><span style=\"font-weight: 400;\"> Yes. Each WordPress install gets its own MariaDB database, PHP-FPM pool, Nginx server block, and SSL certificate. Assign each site a different Redis database index to prevent cache collisions. With 4 GB RAM and properly tuned PHP-FPM pools, 5 to 8 WordPress sites run without performance issues between them.<\/span><\/p>\n<p><b>How do I back up multiple websites on one VPS?<\/b><span style=\"font-weight: 400;\"> Run a cron-scheduled bash script that dumps each database with <\/span><span style=\"font-weight: 400;\">mysqldump<\/span><span style=\"font-weight: 400;\">, archives each site&#8217;s document root with <\/span><span style=\"font-weight: 400;\">tar<\/span><span style=\"font-weight: 400;\">, and syncs the output to off-site object storage with <\/span><span style=\"font-weight: 400;\">rclone<\/span><span style=\"font-weight: 400;\">. Schedule daily runs at a low-traffic hour (3 AM works well). Test restores every 90 days on a test domain.<\/span><\/p>\n<p><b>Is hosting multiple sites on one VPS secure?<\/b><span style=\"font-weight: 400;\"> Yes, with proper isolation in place. Run each site under a separate Linux user with its own PHP-FPM pool. Set file permissions so no user can read another&#8217;s files or credentials. Enable UFW, install Fail2Ban, and disable Nginx version disclosure. The main risk in an improperly configured setup is lateral movement after a single site compromise. Per-user isolation removes that risk.<\/span><\/p>\n<p><b>How does DNS work when hosting multiple domains on one VPS?<\/b><span style=\"font-weight: 400;\"> Create an A record for each domain pointing to your VPS&#8217;s public IP. When a visitor loads a domain, their browser sends an HTTP <\/span><span style=\"font-weight: 400;\">Host<\/span><span style=\"font-weight: 400;\"> header with that domain name. Nginx reads the header and matches it against <\/span><span style=\"font-weight: 400;\">server_name<\/span><span style=\"font-weight: 400;\"> directives to route the request to the correct document root and configuration block.<\/span><\/p>\n<p><b>At what point should I upgrade from a VPS to a dedicated server?<\/b><span style=\"font-weight: 400;\"> Move up once any site consistently exceeds 50,000 monthly visitors, RAM usage stays above 85% during peak hours, or compliance requirements demand physical hardware isolation. Atal Networks&#8217; <\/span><a href=\"https:\/\/atalnetworks.com\/dedicated-servers\/\"><span style=\"font-weight: 400;\">dedicated server plans<\/span><\/a><span style=\"font-weight: 400;\"> provide isolated Intel Xeon hardware with NVMe SSD and 10Gbps ports for workloads that have outgrown shared VPS resources.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Run Multiple Sites Without Overpaying for Hosting<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Hosting multiple websites on one VPS is the right setup for developers, agencies, and businesses running a portfolio of low-to-medium traffic sites. The configuration described here, Nginx server blocks, per-site PHP-FPM pools, separate Linux users, Redis caching, SSL via Certbot, and automated backups, scales from a two-site personal setup to a 15-client agency stack without fundamental changes. You adjust resource allocation, not architecture.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The key is starting with infrastructure that gives you full root access, consistent KVM-isolated resources, and room to scale when individual sites grow beyond what a shared VPS can support.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Atal Networks&#8217; <\/span><a href=\"https:\/\/atalnetworks.com\/linux-vps-hosting\/\"><span style=\"font-weight: 400;\">Linux VPS hosting<\/span><\/a><span style=\"font-weight: 400;\"> plans start at $5.25\/month with KVM virtualization, full root access, and SSD storage across 213+ global data centers. All plans run on Dell hardware with Intel Xeon processors and ship with a 99.99% uptime SLA backed by our 100% network guarantee. Over 35,000 businesses across 196 countries run their infrastructure with us.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Once your sites outgrow a shared VPS, our <\/span><a href=\"https:\/\/atalnetworks.com\/dedicated-servers\/\"><span style=\"font-weight: 400;\">dedicated servers<\/span><\/a><span style=\"font-weight: 400;\"> and <\/span><a href=\"https:\/\/atalnetworks.com\/premium-bare-metal-servers\/\"><span style=\"font-weight: 400;\">bare metal servers<\/span><\/a><span style=\"font-weight: 400;\"> are ready. No migration complexity. Same infrastructure provider, same support team, same SLA terms.<\/span><\/p>\n<p><a href=\"https:\/\/atalnetworks.com\/vps\/\"><b>Deploy Your VPS Today<\/b><\/a><b> | <\/b><a href=\"https:\/\/atalnetworks.com\/dedicated-servers\/\"><b>View Dedicated Server Plans<\/b><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Paying for five separate hosting plans to run five low-traffic sites is money left on the table. One properly configured [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":23890,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[1],"tags":[],"class_list":["post-23884","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-grade-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/posts\/23884","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/comments?post=23884"}],"version-history":[{"count":7,"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/posts\/23884\/revisions"}],"predecessor-version":[{"id":23894,"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/posts\/23884\/revisions\/23894"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/media\/23890"}],"wp:attachment":[{"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/media?parent=23884"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/categories?post=23884"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/atalnetworks.com\/es\/wp-json\/wp\/v2\/tags?post=23884"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}