Automatic agency subdomains
Do this once. After it, every approved agency gets name.quorlasocial.com with HTTPS automatically, without anyone touching Hestia.
What this changes. Today a new agency's workspace (container, data, port and welcome email) is created by the provisioning script, but its web address needs three manual steps in Hestia: DNS, SSL and nginx. This setup replaces those with:
- one wildcard DNS record
- one wildcard certificate
- one nginx rule that reads
nginx-tenants.map(the script already keeps that file up to date)
Nothing changes for app.quorlasocial.com or clickstocart.quorlasocial.com: nginx always prefers an exact server name over the wildcard.
0Check and back up
sudo nginx -t nginx -v sudo /usr/local/hestia/bin/v-list-web-domain user quorlasocial.com sudo /usr/local/hestia/bin/v-list-dns-records user quorlasocial.com | grep -E '^ID|\*' which nginx systemctl sudo tar -czf ~/nginx-backup-$(date +%F).tar.gz /etc/nginx
nginx -tsays the syntax is ok.- The web-domain line tells you whether
quorlasocial.comis a web domain on Hestia. Note the answer, because it decides step 2. - The DNS line tells you whether a
*record already exists. - Note the two paths from
which, usually/usr/sbin/nginxand/usr/bin/systemctl.
1Wildcard DNS
Every anything.quorlasocial.com then points at this server. Skip this step if step 0 already showed a * record.
sudo /usr/local/hestia/bin/v-add-dns-record user quorlasocial.com '*' A 72.62.169.43 dig +short test123.quorlasocial.com @8.8.8.8
72.62.169.43. It can take a few minutes to appear.2Wildcard SSL certificate
Hestia can issue a *.quorlasocial.com certificate because the DNS zone is on this server, and it renews the certificate by itself.
Only if step 0 showed that quorlasocial.com is not a web domain yet, add it first. Do this only if the bare domain isn't hosted somewhere else (for example a marketing site); if it is, ask Claude first.
sudo /usr/local/hestia/bin/v-add-web-domain user quorlasocial.com
Then add the wildcard alias and issue the certificate:
sudo /usr/local/hestia/bin/v-add-web-domain-alias user quorlasocial.com '*.quorlasocial.com' sudo /usr/local/hestia/bin/v-add-letsencrypt-domain user quorlasocial.com '*.quorlasocial.com' sudo openssl x509 -in /home/user/conf/web/quorlasocial.com/ssl/quorlasocial.com.pem -noout -subject -ext subjectAltName -enddate
DNS:*.quorlasocial.com and an end date about 90 days away.3One nginx rule for every agency
This file lives outside Hestia's per-domain folders, so Hestia rebuilds don't overwrite it. It looks up the agency's port by the subdomain. Addresses that aren't in the map get a 404.
touch /home/dev/APPS/quorla-tenants/nginx-tenants.map
sudo tee /etc/nginx/conf.d/quorla-tenants.conf >/dev/null <<'EOF'
map $host $quorla_tenant {
default "";
include /home/dev/APPS/quorla-tenants/nginx-tenants.map;
}
server {
listen 72.62.169.43:80;
server_name *.quorlasocial.com;
return 301 https://$host$request_uri;
}
server {
listen 72.62.169.43:443 ssl;
server_name *.quorlasocial.com;
ssl_certificate /home/user/conf/web/quorlasocial.com/ssl/quorlasocial.com.pem;
ssl_certificate_key /home/user/conf/web/quorlasocial.com/ssl/quorlasocial.com.key;
client_max_body_size 500m;
proxy_read_timeout 600s;
location / {
if ($quorla_tenant = "") { return 404; }
proxy_pass http://$quorla_tenant;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
EOF
sudo nginx -t && sudo systemctl reload nginx
syntax is ok. If nginx warns that *.quorlasocial.com is a "conflicting server name", the step 2 alias made Hestia's quorlasocial.com config claim the wildcard too. Paste the warning to Claude; the fix is a one-line change.
4Let the script reload nginx
The script runs as dev. It needs exactly two root commands and nothing else. Use the paths from step 0 if they differ.
echo 'dev ALL=(root) NOPASSWD: /usr/sbin/nginx -t, /usr/bin/systemctl reload nginx' | sudo tee /etc/sudoers.d/quorla sudo chmod 440 /etc/sudoers.d/quorla sudo visudo -c sudo -n nginx -t && echo "sudo rule works"
parsed OK, then sudo rule works, without being asked for a password.5Run the script every 5 minutes
It picks up approved agencies, creates their workspaces, moves agencies onto new versions after a deploy, and refreshes the nginx map.
bash /home/dev/APPS/quorlasocial/deploy/provision-tenants.sh cat /home/dev/APPS/quorla-tenants/nginx-tenants.map ( crontab -l 2>/dev/null | grep -v provision-tenants.sh; echo '*/5 * * * * /home/dev/APPS/quorlasocial/deploy/provision-tenants.sh >> /home/dev/APPS/quorla-tenants/provision.log 2>&1' ) | crontab - crontab -l
clickstocart.quorlasocial.com 127.0.0.1:4400;, the script logs nginx reloaded the first time, and the cron line is listed.6Test
curl -sI https://app.quorlasocial.com | head -1 curl -sI https://clickstocart.quorlasocial.com | head -1 curl -sI https://nobody-here.quorlasocial.com | head -1
Then do a real end-to-end test:
- Sign up a test agency at
https://app.quorlasocial.com/signup. - Approve it in Admin → Agencies.
- Wait up to 5 minutes, then open
https://<test>.quorlasocial.com. The welcome email's link should work. - Remove the test agency in Agencies afterwards. Its data is archived.
Undo
sudo rm /etc/nginx/conf.d/quorla-tenants.conf sudo nginx -t && sudo systemctl reload nginx crontab -l | grep -v provision-tenants.sh | crontab -
The DNS record and the certificate are harmless to leave in place. Existing workspaces keep running either way.