🧭 Subdomain Deployment Guide (Reusable Template)
⚙️ Server Context
| Component | Example |
|---|---|
| OS | Ubuntu 24.04 LTS |
| Web server | Apache 2.4 |
| PHP | 8.3.x |
| Database | MariaDB 10.11 |
| Project root | /var/www/sites/ |
| Main user | <your-linux-user> |
| Shared group | <shared-group> (e.g., webdev) |
🪪 1. Define Project Variables
PROJECT=<project-folder-name> # e.g., blog-platform
DOMAIN=<subdomain.example.net> # e.g., blog.example.net
DB_NAME=<database_name> # e.g., blog_db
DB_USER=<database_user> # e.g., blog_user
DB_PASS=<StrongPassword2025>
EMAIL=<admin@example.net>
🌐 2. Configure DNS
In your DNS provider’s panel:
| Field | Value |
|---|---|
| Type | A |
| Name | subdomain (e.g., blog) |
| Points to | VPS public IP |
| TTL | default (3600) |
Test propagation:
host $DOMAIN
🗂️ 3. Create the Project Directory
sudo mkdir -p /var/www/sites/$PROJECT
sudo chown -R $USER:$GROUP /var/www/sites/$PROJECT
sudo chmod 2775 /var/www/sites/$PROJECT
(the 2 = setgid bit keeps group inheritance)
If using a Git repo:
cd /var/www/sites/$PROJECT
git clone <repository-url> .
composer install --no-interaction --prefer-dist --no-progress
🌐 4. Apache Virtual Host (HTTP)
Create /etc/apache2/sites-available/$DOMAIN.conf:
sudo tee /etc/apache2/sites-available/$DOMAIN.conf >/dev/null <<APACHE
<VirtualHost *:80>
ServerName $DOMAIN
DocumentRoot /var/www/sites/$PROJECT/web
<Directory /var/www/sites/$PROJECT/web>
AllowOverride All
Require all granted
Options FollowSymLinks
</Directory>
ErrorLog \${APACHE_LOG_DIR}/$PROJECT-error.log
CustomLog \${APACHE_LOG_DIR}/$PROJECT-access.log combined
</VirtualHost>
APACHE
Enable the site:
sudo a2enmod rewrite headers env
sudo a2ensite $DOMAIN
sudo systemctl reload apache2
Check:
apache2ctl -S | grep $DOMAIN
curl -I -H "Host: $DOMAIN" http://127.0.0.1
🧰 5. Database Setup
sudo mysql
CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';
GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';
FLUSH PRIVILEGES;
EXIT;
🔧 6. Application Configuration (e.g., Drupal)
Edit /var/www/sites/$PROJECT/web/sites/default/settings.php:
$databases['default']['default'] = [
'database' => '<database_name>',
'username' => '<database_user>',
'password' => '<database_password>',
'host' => '127.0.0.1',
'port' => '3306',
'driver' => 'mysql',
'prefix' => '',
];
$settings['trusted_host_patterns'] = ['^<escaped-subdomain>\.<escaped-domain>\.net$'];
$settings['file_private_path'] = '/var/www/sites/<project-folder>/private';
$settings['config_sync_directory'] = '../config/sync';
Lock permissions:
sudo chmod 640 web/sites/default/settings.php
sudo setfacl -m u:www-data:r web/sites/default/settings.php
🧱 7. Permissions & ACLs
sudo chgrp -R <shared-group> /var/www/sites/$PROJECT
sudo find /var/www/sites/$PROJECT -type d -exec chmod 2775 {} \;
sudo find /var/www/sites/$PROJECT -type f -exec chmod 664 {} \;
sudo setfacl -R -m g:<shared-group>:rwX,u:www-data:rwX /var/www/sites/$PROJECT
sudo setfacl -dR -m g:<shared-group>:rwX,u:www-data:rwX /var/www/sites/$PROJECT
🔒 8. HTTPS with Certbot
After verifying HTTP works:
sudo certbot --apache \
-d $DOMAIN \
--redirect \
-m $EMAIL \
--agree-tos -n
Verify:
apache2ctl -S | grep -E "$DOMAIN|:443"
curl -I https://$DOMAIN
sudo certbot renew --dry-run
🧹 9. Maintenance Commands
| Purpose | Command |
|---|---|
| Reload Apache after edits | sudo systemctl reload apache2 |
| Disable a site | sudo a2dissite <domain> |
| Enable a site | sudo a2ensite <domain> |
| Check configuration | sudo apache2ctl configtest |
| View logs | sudo tail -f /var/log/apache2/*error.log |
| Test SSL renewal | sudo certbot renew --dry-run |
✅ 10. Verification Checklist
-
DNS resolves to VPS IP
-
HTTP returns 200/302/404 (not 500)
-
HTTPS redirects and cert is valid
-
Apache logs show no critical errors
-
File permissions:
daniel:webdev, ACLs applied -
Database credentials verified
-
Cron (optional):
( crontab -l 2>/dev/null; echo "*/15 * * * * cd /var/www/sites/$PROJECT/web && /usr/bin/php ../vendor/bin/drush cron -q" ) | crontab -
🏁 Notes
- Always confirm that
.htaccessis respected (requiresAllowOverride All). - Each subdomain gets its own Apache vhost and Let’s Encrypt certificate (
-le-ssl.conf). - Do not reuse DB users between unrelated projects for security.
- Keep a backup of
settings.phpandprivate/outside webroot.