Understanding its Relation to Document Roots and Suitability
This document provides a detailed explanation of the "Option 1: Environment-First, then by Ownership/Purpose" directory structure. We will explore how it deviates from and improves upon default web server document root configurations, and why it is particularly well-suited for hosting environments that manage:
The core philosophy of this structure is to provide clear separation for deployment environments (production, staging, development) and then to categorize sites by their ownership and purpose, using conventional and understandable directory names.
The "Option 1: Environment-First, then by Ownership/Purpose (Revised with Conventional Names)" structure is as follows:
/var/www/
├── production/
│ ├── company/ # Your company's sites & applications
│ │ ├── internal/ # Non-public facing or internal tools
│ │ │ └── tool.mycompany.local/
│ │ │ ├── public_html/ # DocumentRoot
│ │ │ └── private/ # Logs, configs, backups
│ │ ├── public/ # Your main public-facing company websites
│ │ │ └── mycompany.com/
│ │ │ ├── public_html/
│ │ │ └── private/
│ │ │ └── ourbrand.com/
│ │ │ ├── public_html/
│ │ │ └── private/
│ ├── clients/ # For reseller clients or managed hosting clients
│ │ └── clientA-ID001/
│ │ ├── example-client-site.com/
│ │ │ ├── public_html/
│ │ │ └── private/
│ │ ├── another-client-domain.net/
│ │ │ ├── public_html/
│ │ │ └── private/
│ │ └── clientB-ID002/
│ │ └── clientb-main.org/
│ │ ├── public_html/
│ │ └── private/
│ ├── users/ # For regular users with personal domains
│ │ └── userX-UID101/
│ │ ├── myblog.com/
│ │ │ ├── public_html/
│ │ │ └── private/
│ │ └── personalproject.dev/
│ │ ├── public_html/
│ │ └── private/
│ └── apps/ # If you host shared SaaS instances, etc.
│ └── sharedcrm/
│ ├── public_html/
│ └── data/
├── staging/ # Staging would mirror the production structure
│ ├── company/ # ... (similar sub-structure)
│ ├── clients/ # ...
│ ├── users/ # ...
│ └── apps/ # ...
├── development/ # Development would also mirror
│ ├── company/ # ...
│ ├── clients/ # ...
│ ├── users/ # ...
│ └── sandboxes/
│ └── dev_jane/
│ └── experimental_project/public_html/
# Associated Service/Automation User Homes (example)
/home/sys_deploy_bot/
/home/sys_backup_svc/
# Site-specific automation scripts might reside in:
# /var/www/production/clients/clientA-ID001/example-client-site.com/scripts/
Each individual website (e.g., mycompany.com
, example-client-site.com
) has its web-accessible files located within its respective public_html/
subdirectory. Non-web-accessible files like logs, backups, and private configuration scripts are typically stored in the sibling private/
directory or other designated non-public directories.
A DocumentRoot is a critical directive in web server configuration (e.g., Apache, Nginx). It specifies the top-level directory in the server's file system from which it will serve files for a particular website or domain. When a user requests http://example.com/page.html
, the web server looks for page.html
within the DocumentRoot
defined for example.com
.
/var/www/html/
./usr/share/nginx/html/
or /var/www/html/
.These defaults are suitable for a single site or as a fallback. For hosting multiple distinct websites, Virtual Hosts (Apache) or Server Blocks (Nginx) are essential. Each virtual host/server block defines its own specific DocumentRoot
(Apache) or root
(Nginx) directive.
The "Option 1" directory structure is designed with the explicit use of virtual hosts/server blocks for every website. It moves away from a single, global default DocumentRoot
.
/var/www/
: The /var/www/
directory itself, or even /var/www/production/
, are not intended to be global document roots. They are organizational containers.public_html/
as Document Root: For every individual website, its DocumentRoot
(Apache) or root
(Nginx) will always be its specific public_html/
subdirectory. This is the core principle.
mycompany.com
, the web server will be configured to serve files only from /var/www/production/company/public/mycompany.com/public_html/
.public_html/
as the document root ensures that only files intended to be web-accessible are served. Files in sibling directories like private/
(containing logs, backups, configurations) or parent directories are not directly accessible via a web browser, which is crucial for security.This structure is particularly well-suited for managing multiple company sites, client sites (including reseller scenarios), and individual user sites due to:
production/
, staging/
, development/
):
company/
, clients/
, users/
, apps/
):
company/
→ internal/
, public/
): Manages diverse company web properties distinctly. Each company site (e.g., mycompany.com
) has its own isolated directory.clients/
): Supports a reseller model where each client (e.g., clientA-ID001/
) can host multiple domains (e.g., example-client-site.com/
, another-client-domain.net/
), each with its own public_html/
and private/
areas.users/
): Provides dedicated space for users to host multiple personal domains.apps/
): For centrally managed shared applications.private/
Directory: Reinforces separation of public web content from sensitive backend data.Below are explicit examples showing how different paths within the Option 1 structure map to DocumentRoot
(Apache) and root
(Nginx) for various site types in the production
environment.
mycompany.com
)mycompany.com
/var/www/production/company/public/mycompany.com/
/var/www/production/company/public/mycompany.com/public_html/
/var/www/production/company/public/mycompany.com/private/
<VirtualHost>
block):<VirtualHost *:80>
ServerAdmin webmaster@mycompany.com
ServerName mycompany.com
ServerAlias www.mycompany.com
DocumentRoot /var/www/production/company/public/mycompany.com/public_html
ErrorLog /var/www/production/company/public/mycompany.com/private/logs/error.log
CustomLog /var/www/production/company/public/mycompany.com/private/logs/access.log combined
<Directory /var/www/production/company/public/mycompany.com/public_html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Additional security for private directory (though it's outside DocumentRoot)
<Directory /var/www/production/company/public/mycompany.com/private>
Require all denied
</Directory>
</VirtualHost>
server
block):server {
listen 80;
listen [::]:80;
server_name mycompany.com www.mycompany.com;
root /var/www/production/company/public/mycompany.com/public_html;
index index.html index.htm index.php;
access_log /var/www/production/company/public/mycompany.com/private/logs/access.log;
error_log /var/www/production/company/public/mycompany.com/private/logs/error.log;
location / {
try_files $uri $uri/ /index.php?$query_string; # Example for PHP apps
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# Adjust to your PHP-FPM version/socket, e.g.:
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
}
# Deny access to .htaccess or other sensitive files if they exist in public_html
location ~ /\.ht {
deny all;
}
}
example-client-site.com
for clientA-ID001
)example-client-site.com
clientA-ID001
/var/www/production/clients/clientA-ID001/example-client-site.com/
/var/www/production/clients/clientA-ID001/example-client-site.com/public_html/
/var/www/production/clients/clientA-ID001/example-client-site.com/private/
<VirtualHost *:80>
ServerAdmin admin@example-client-site.com
ServerName example-client-site.com
ServerAlias www.example-client-site.com
DocumentRoot /var/www/production/clients/clientA-ID001/example-client-site.com/public_html
ErrorLog /var/www/production/clients/clientA-ID001/example-client-site.com/private/logs/error.log
CustomLog /var/www/production/clients/clientA-ID001/example-client-site.com/private/logs/access.log combined
<Directory /var/www/production/clients/clientA-ID001/example-client-site.com/public_html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
# Consider specific PHP-FPM user for this client if using PHP
# <FilesMatch \.php$>
# SetHandler "proxy:unix:/var/run/php/php8.2-fpm-clientA.sock|fcgi://localhost/"
# </FilesMatch>
</Directory>
<Directory /var/www/production/clients/clientA-ID001/example-client-site.com/private>
Require all denied
</Directory>
</VirtualHost>
server {
listen 80;
listen [::]:80;
server_name example-client-site.com www.example-client-site.com;
root /var/www/production/clients/clientA-ID001/example-client-site.com/public_html;
index index.html index.htm index.php;
access_log /var/www/production/clients/clientA-ID001/example-client-site.com/private/logs/access.log;
error_log /var/www/production/clients/clientA-ID001/example-client-site.com/private/logs/error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# Consider a separate PHP-FPM pool for this client for better isolation
# fastcgi_pass unix:/var/run/php/php8.2-fpm-clientA.sock;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; # Default if not using per-client pools
}
location ~ /\.ht {
deny all;
}
}
myblog.com
for userX-UID101
)myblog.com
userX-UID101
/var/www/production/users/userX-UID101/myblog.com/
/var/www/production/users/userX-UID101/myblog.com/public_html/
/var/www/production/users/userX-UID101/myblog.com/private/
<VirtualHost *:80>
ServerAdmin userx@myblog.com
ServerName myblog.com
ServerAlias www.myblog.com
DocumentRoot /var/www/production/users/userX-UID101/myblog.com/public_html
ErrorLog /var/www/production/users/userX-UID101/myblog.com/private/logs/error.log
CustomLog /var/www/production/users/userX-UID101/myblog.com/private/logs/access.log combined
<Directory /var/www/production/users/userX-UID101/myblog.com/public_html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<Directory /var/www/production/users/userX-UID101/myblog.com/private>
Require all denied
</Directory>
</VirtualHost>
server {
listen 80;
listen [::]:80;
server_name myblog.com www.myblog.com;
root /var/www/production/users/userX-UID101/myblog.com/public_html;
index index.html index.htm index.php;
access_log /var/www/production/users/userX-UID101/myblog.com/private/logs/access.log;
error_log /var/www/production/users/userX-UID101/myblog.com/private/logs/error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
In all these examples, the key takeaway is the consistent use of public_html/
as the web-accessible root, with private/
(or similar) for non-web data. The web server is explicitly told where to find the public_html/
for each domain.
The "Option 1: Environment-First, then by Ownership/Purpose" directory structure, when paired with correctly configured virtual hosts (Apache) or server blocks (Nginx), provides a highly organized, scalable, and secure method for hosting diverse websites. By defining a specific DocumentRoot
(or root
) pointing to each site's public_html/
directory, you ensure clear separation of web-accessible content from private data, which is fundamental to secure web server administration. This structured approach simplifies management, enhances security, and supports a robust development-to-production workflow.