Somewhere around my fifth client project, I stopped spinning up a new server for every single website. It was expensive, wasteful, and honestly unnecessary — a single modest VPS running Apache can comfortably host a dozen or more low-to-medium traffic sites if it’s configured properly. In this post, I’ll walk through exactly how I configure Apache to serve multiple, completely independent domains from one server.
What Multi-Domain Hosting Is and Why It Matters
Apache supports something called name-based virtual hosting, which lets a single server, single IP address, and single Apache instance serve entirely different websites based on the Host header sent by the browser. This is different from needing a dedicated server or IP per domain — it’s the same technology that powers most shared hosting providers.
I use this setup for:
- Hosting multiple client websites cost-effectively
- Running personal projects alongside client work on one box
- Separating a company’s main site, blog, and marketing landing pages
- Testing multi-tenant application architectures
Prerequisites
- A Linux server with Apache installed
- Root or sudo access
- Multiple domain names, each with DNS A records pointing to the server’s IP
- Basic familiarity with the command line
Step 1: Plan Your Directory Structure
Before touching Apache config at all, I lay out a consistent folder structure. I’ve found this convention works well:
sudo mkdir -p /var/www/site1.com/public_html
sudo mkdir -p /var/www/site2.com/public_html
sudo mkdir -p /var/www/site3.com/public_html
Consistency here saves a lot of confusion later when you’re managing ten-plus sites.
Step 2: Point DNS at Your Server
For each domain, I create an A record pointing to the server’s public IP address:
site1.com. A 203.0.113.10
site2.com. A 203.0.113.10
site3.com. A 203.0.113.10
DNS propagation can take anywhere from a few minutes to 24-48 hours, so I usually start this step first and work on Apache config while I wait.
Step 3: Create a Virtual Host File for Each Domain
On Debian/Ubuntu, I keep configs organized under /etc/apache2/sites-available/, one file per domain:
# /etc/apache2/sites-available/site1.com.conf
<VirtualHost *:80>
ServerName site1.com
ServerAlias www.site1.com
DocumentRoot /var/www/site1.com/public_html
<Directory /var/www/site1.com/public_html>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/site1.com_error.log
CustomLog ${APACHE_LOG_DIR}/site1.com_access.log combined
</VirtualHost>
I repeat this pattern for site2.com and site3.com, changing only the ServerName, DocumentRoot, and log file names.
Step 4: Enable Each Site
sudo a2ensite site1.com.conf
sudo a2ensite site2.com.conf
sudo a2ensite site3.com.conf
On CentOS/RHEL, I skip this step — files placed directly in /etc/httpd/conf.d/ with a .conf extension load automatically.
Step 5: Validate and Reload Apache
sudo apache2ctl configtest
sudo systemctl reload apache2
I always run configtest before reloading — catching a syntax error here takes seconds, while debugging a failed reload with three sites down takes a lot longer.
Step 6: Confirm All Sites Loaded Correctly
sudo apache2ctl -S
This prints every virtual host Apache knows about along with its ServerName, port, and the config file it came from. I scan this output to confirm all three domains are present and none are conflicting.
Step 7: Add HTTPS for Each Domain
For real production use, I add SSL to each domain right away using Certbot, which handles certificate issuance and vhost updates automatically:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d site1.com -d www.site1.com
sudo certbot --apache -d site2.com -d www.site2.com
sudo certbot --apache -d site3.com -d www.site3.com
Each domain gets its own certificate, and thanks to SNI, they can all be served securely from the same IP and port 443.
Real-World Example: A Small Agency Server
I currently run six low-traffic client sites off a single 2GB RAM VPS. Each one has its own directory, its own vhost file, its own SSL certificate via Certbot, and its own log files. Resource usage stays modest because none of these sites see huge simultaneous traffic, and Apache’s process/thread model handles the concurrent connections without issue. If any single site started seeing serious traffic, that would be the point where I’d consider moving it to its own server.
Step 8: Managing Server-Wide Resource Limits Across Sites
Once several domains share the same Apache instance, I keep a close eye on server-wide resource limits so no single site can starve the others. I set sensible MaxRequestWorkers and connection timeout values in the MPM configuration, and where PHP is involved, I configure per-site memory limits in each site’s PHP-FPM pool rather than relying on a single global PHP configuration shared across everything:
; in a per-site PHP-FPM pool config
pm.max_children = 10
php_admin_value[memory_limit] = 128M
This means a runaway script or traffic spike on one client’s site is contained, rather than consuming resources that the other domains on the server also depend on.
Step 9: Documenting Which Domain Lives Where
With more than a few domains on one server, I keep a simple internal document (even just a plain text file or spreadsheet) listing every domain, its document root, its SSL certificate expiry, and which client or project it belongs to. It sounds almost too simple to mention, but on a server with a dozen or more sites, this kind of basic inventory has saved me repeatedly when doing maintenance, planning migrations, or onboarding someone else to help manage the server.
Troubleshooting Common Issues
One domain shows another site’s content Nearly always a ServerName/ServerAlias mismatch, or the site simply isn’t enabled. Check apache2ctl -S.
New site returns a 404 or Apache default page DNS may not have propagated yet, or the vhost file wasn’t enabled. I check dig site1.com +short to confirm DNS is resolving correctly.
Apache runs out of memory with many sites On resource-constrained VPS instances, I check the MPM (Multi-Processing Module) configuration and consider switching to mpm_event if not already in use, which handles concurrent connections more efficiently than mpm_prefork.
Permission denied errors on one site but not others Usually an ownership mismatch on that specific site’s directory. I re-run chown -R www-data:www-data on the affected folder.
Security Best Practices
- I isolate each site’s files under its own directory with correct ownership, so a compromise in one site’s code doesn’t automatically expose another’s files.
- I keep separate error and access logs per domain, which makes it much easier to spot if one particular site is being targeted.
- I set a proper default/catch-all virtual host so requests to the raw IP don’t accidentally serve one client’s content (see my related post on default virtual hosts).
- If sites run PHP, I consider separate PHP-FPM pools per site running as different system users, so a vulnerability in one application can’t read another’s files.
Performance Optimization Tips
- I enable
mpm_eventfor better concurrency handling across many low-to-medium traffic sites:
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo systemctl restart apache2
- I enable caching headers (
mod_expires,mod_headers) per site to reduce repeat load on the server. - I monitor resource usage with
htoporapachetopregularly rather than waiting for something to break.
Step 10: Deciding When It’s Time to Split a Site onto Its Own Server
Multi-domain hosting works great until one site outgrows the shared environment. I watch for a few warning signs that tell me it’s time to migrate a specific domain off the shared server: consistently high CPU or memory usage traceable to one site, traffic spikes that start affecting the responsiveness of other sites on the box, or a client’s compliance requirements changing to demand dedicated infrastructure. When that happens, the same virtual host configuration I built for shared hosting usually transfers almost directly onto a new, dedicated server, since the vhost block itself doesn’t fundamentally change — only the underlying infrastructure it runs on does.
Frequently Asked Questions
How many domains can one Apache server realistically host? There’s no hard technical limit — it comes down to available resources (CPU, RAM, disk I/O) and each site’s actual traffic level. Dozens of low-traffic sites on a modest VPS is very common.
Do I need a separate IP address for each domain? No. Name-based virtual hosting with SNI lets you serve many domains, including over HTTPS, from a single IP.
What happens if two sites get equally busy at the same time? Apache’s worker processes/threads are shared across all vhosts on the server, so heavy traffic to one site can affect others. This is the main reason to eventually split very high-traffic sites onto dedicated infrastructure.
Can I mix PHP and static-only sites on the same server? Yes, this is completely normal — each virtual host is configured independently, so one can run PHP-FPM while another serves pure static files.
Summary and Key Takeaways
Hosting multiple domains on a single Apache server is really just repeating the same pattern for each site: a dedicated directory, a dedicated virtual host file, correct DNS, and (for production) its own SSL certificate. Keeping a consistent naming and folder convention, and always validating with configtest and apache2ctl -S, makes scaling from two sites to twenty a lot less painful.