Shared web hosting extracted from the platform monolith, with CI deploy to /var/www/ladill-hosting matching Bird/Domains/Email. Co-authored-by: Cursor <cursoragent@cursor.com>
4.1 KiB
Ladill Hosting — deploy & cutover runbook
Standalone app for shared web hosting at hosting.ladill.com (single-domain,
multi-domain, WordPress). VPS and dedicated servers are a separate product —
Ladill Servers at servers.ladill.com (not part of this app).
It trusts auth.ladill.com for login (OIDC) and bills the one platform
UserWallet over the §4-A Billing API. Provisioning runs against Contabo nodes
and the shared hosting infrastructure (same stack as the monolith phase).
Mirrors the Ladill Domains / Email deployment model (app-slot + own DB + Gitea CI).
0. Prerequisites
| Secret | Where it comes from |
|---|---|
CONTABO_CLIENT_ID / CONTABO_CLIENT_SECRET / CONTABO_API_USER / CONTABO_API_PASSWORD |
Contabo API credentials |
PDNS_API_URL / PDNS_API_KEY |
PowerDNS (zone management) |
PAYSTACK_SECRET_KEY / PAYSTACK_PUBLIC_KEY |
Paystack dashboard |
LADILL_SSO_CLIENT_ID / LADILL_SSO_CLIENT_SECRET |
step 3 |
BILLING_API_KEY_HOSTING |
step 4 (must match platform env) |
IDENTITY_API_KEY_HOSTING |
step 4 (must match platform env) |
1. Gitea repo + CI
- Create repo
isaacclad/ladill-hostingin Gitea. - Push this repo (
git remote add origin … && git push -u origin main). - Ensure an instance-level Actions runner exists (shared with Bird/Domains/Email).
- CI secrets (
.gitea/workflows/deploy.yml):SSH_HOST,SSH_PORT,SSH_USER,APP_ROOT=/var/www/ladill-hosting, deploy SSH key.
2. Server app-slot + database
sudo install -d -o deploy -g www-data /var/www/ladill-hosting
sudo mysql -e "CREATE DATABASE ladill_hosting CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql -e "CREATE USER 'ladill_hosting'@'127.0.0.1' IDENTIFIED BY '<pw>';"
sudo mysql -e "GRANT ALL ON ladill_hosting.* TO 'ladill_hosting'@'127.0.0.1'; FLUSH PRIVILEGES;"
Create /var/www/ladill-hosting/.env from .env.example, fill secrets,
then php artisan key:generate.
3. Register the OIDC client (platform / auth.ladill.com)
# On the monolith
php artisan passport:client \
--name="Ladill Hosting" \
--redirect_uri="https://hosting.ladill.com/sso/callback"
Set LADILL_SSO_CLIENT_ID / LADILL_SSO_CLIENT_SECRET in this app's .env.
4. Platform integration
On the platform .env:
BILLING_API_KEY_HOSTING=<same as this app>
IDENTITY_API_KEY_HOSTING=<same as this app>
RP_HOSTING_FRONTCHANNEL_LOGOUT=https://hosting.ladill.com/sso/logout-frontchannel
LADILL_HOSTING_APP_URL=https://hosting.ladill.com
Then php artisan config:cache on the platform.
5. nginx + TLS
sudo deployment/setup-service-subdomain-nginx.sh hosting --app ladill-hosting
DNS hosting.ladill.com is already in config/pdns.php → dns:sync-subdomains on deploy.
6. First deploy
Push to main (CI) or run deploy manually, then:
cd /var/www/ladill-hosting/current
php artisan migrate --force
php artisan db:seed --class=HostingProductSeeder --force
php artisan config:cache route:cache view:cache
chgrp -R www-data storage bootstrap/cache && chmod -R g+rwX storage bootstrap/cache
Ensure a queue worker runs (queue:work via supervisor).
7. Zero-loss migration
# a) On the platform — export customer hosting rows
php artisan hosting:export --out=/tmp/hosting-export.json
# b) Copy to the hosting app server, dry-run import
php artisan hosting:import /tmp/hosting-export.json
# c) Apply
php artisan hosting:import /tmp/hosting-export.json --commit
Re-running import is idempotent (upserts by original platform id).
8. Smoke test
https://hosting.ladill.com/up→ 200- Logged-out → SSO redirect to auth.ladill.com
- Logged-in → dashboard lists imported accounts
- Open an account → panel loads (files, domains, SSL)
- Buy a test shared hosting plan end-to-end (wallet)
- Log out → SLO clears hosting session
9. Decommission platform hosting UI (after burn-in)
Account sidebar + /hosting/* on account.ladill.com already redirect to
hosting.ladill.com. After burn-in, remove remaining platform hosting models/jobs
once the hosting app is the sole writer.