All about virtual hosts
A virtual host serves a domain, and optional aliases, over HTTP from a directory on disk.
Creating a project creates a virtual host per environment and attaches the right backend to it, so you only come here when you need something a project doesn't set up, such as an extra domain or a site outside a project.
This article explains how to create one in Core and the rules that aren't obvious from the form fields.
Where to find virtual hosts
The top-level 'Virtual Hosts' page lists all virtual hosts on your clusters. Click 'Create' to add one.
Creating one
'Domain' and 'UNIX User'
- 'Domain' — the domain name to serve, for example
dropflix.io. - 'UNIX User' — the UNIX user that owns the virtual host. This choice decides which FPM pools and Passenger apps you can attach.
'Language'
Pick from the tile grid. A virtual host can have an FPM pool or a Passenger app, never both — the language tile enforces that.
- 'Static' — only serves files from disk. No dynamic backend. Always available.
- 'PHP' — requests handled by a PHP-FPM pool. Enabled only when the selected UNIX user has at least one FPM pool. Reveals an 'FPM Pool' dropdown.
- 'NodeJS' — requests handled by a Passenger app. Enabled only when the UNIX user has at least one Passenger app. Reveals a 'Passenger App' dropdown.
A tile greyed out? Hover for the tooltip — usually "no FPM pools available for this UNIX user" or "no Passenger apps available for this UNIX user".
Domain uniqueness
The domain must be unique across all virtual hosts and URL redirects on the cluster. It also cannot match the hostname of any node.
The same rule applies to server aliases, which you add after creation: each is unique across all virtual hosts and URL redirects, and cannot repeat the virtual host's own domain.
For every domain and every server alias, Core creates the per-domain settings entry — a domain router — automatically.
After creation
Separate update flows for:
- 'Document Root' — the directory on disk that HTTP requests load files from.
- 'Custom Config' — server-software config injected into the virtual host. The snippet syntax and ordering rules from All about custom config snippets apply here too.
- 'Directives' — for Apache virtual hosts, the
.htaccessoverrides you allow. - 'Language' — change between Static, PHP and NodeJS, or swap the attached FPM pool or Passenger app.
Custom config caveats
On nginx virtual hosts, your custom config goes in the server context.
If the virtual host has any basic authentication realms attached, the auth_basic and auth_basic_user_file directives are reserved. Do not set them in the default context of your custom config — basic auth manages them itself.
Behaviour worth knowing
Symlinks in the document root
A symlink under the document root is only followed when its target is owned by the same user as the document root. nginx serves a 403 otherwise; Apache disallows it by default.
This stops one site's content from being exposed by a symlink it does not own.
Static virtual hosts won't serve .php
When the language is 'Static', so no FPM pool is attached, index.php is removed from the directory index automatically.
This stops .php files from being served as downloads when no PHP backend is there to execute them.
Automatic FPM pool reload on document root symlink swaps
Atomic deploys usually point the document root at a symlink such as /current and swap the target to a new release directory.
The cluster watches each virtual host's document root and, when the symlink's real path changes, reloads the attached FPM pool. The reload invalidates the PHP OPcache and realpath cache, so the new release's code is picked up immediately.
Only fires for virtual hosts with an FPM pool attached. Static and NodeJS virtual hosts do not need it.
Doesn't fire when code lives outside the document root
The watcher only sees changes to the document root itself. If your code lives in a separate directory — document root /public including code from /src — and only the /src symlink changes, the FPM pool is not reloaded.
Simplest fix: lay deploys out so the symlink swap happens at the document root.
Automatic CMS detection
The cluster periodically scans each virtual host's document root for known CMS markers — currently WordPress, detected by wp-includes/version.php. Hits are recorded against the site, so Core can show which CMS runs where.
Detection covers the document root and its parent directory, so atomic-deploy layouts — document root in a release subdirectory, CMS at the parent — are picked up too.
Manually-created records are never removed
Auto-detected records disappear when the markers are gone from disk. CMS records you add manually are left alone, even when the corresponding files cannot be found.
Common setups
- One virtual host per client domain, each owned by a separate account. That isolation means one client's leaked credentials, vulnerable plugin, or runaway script cannot reach another client's files. Wrap pre-launch sites with basic auth or an IP allow-list on the domain router.
- Marketing site, app, admin — one virtual host each. Pin sensitive admin domains to a firewall group so only the office can reach them.
- Storefront separate from staging and internal tools (BI, admin). Cookie-based session leaks between subdomains are easy to introduce; one virtual host per concern keeps them apart.
- Atomic deploys via document-root symlink swaps — the automatic FPM pool reload above handles the OPcache invalidation.