Documentation

🧭 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 .htaccess is respected (requires AllowOverride 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.php and private/ outside webroot.
Contents