Deployment & Infrastructure¶
Last updated: June 2026 (revised — clean-deploy script, asset cleanup step, stale file removal notes)
Infrastructure Overview¶
Browser
└─ HTTPS → mind-meditate.com (Nginx + Let's Encrypt SSL)
├─ /api/* → reverse proxy → Express :5002
├─ /health → reverse proxy → Express :5002
└─ /* → static files from /opt/personality-app/public/
| Component | Detail |
|---|---|
| VPS | Hostinger, IP 76.13.211.100 |
| SSH | ssh root@76.13.211.100 (key auth) |
| Domain | https://mind-meditate.com |
| TLS | Let's Encrypt (Certbot, auto-renew) |
| Web server | Nginx — serves static files + proxies /api/* |
| Backend | Express.js, port 5002, managed by PM2 (personality-app, id 2) |
| Database | MongoDB personality_db on localhost:27017 |
| Node version | v20.20.0 |
| Static files | /opt/personality-app/public/ (Vite dist/ uploaded via SCP) |
| Backend source | /opt/personality-app/src/ |
| Backend env | /opt/personality-app/.env |
Frontend Deploy¶
Build locally and upload to the VPS. Always clear the remote /assets/ folder first — Vite hashes change every build so old files accumulate and are never overwritten.
Manual steps¶
# 1. Build (run from the client folder)
push-location "c:\My Apps\Personality-Test\client"; npm run build; pop-location
# 2. Clear old assets on the VPS (prevents 8000+ stale hashed files)
ssh root@76.13.211.100 "rm -rf /opt/personality-app/public/assets"
# 3. Upload fresh build
scp -r "c:\My Apps\Personality-Test\client\dist\*" root@76.13.211.100:/opt/personality-app/public/
Automated script (recommended)¶
A PowerShell deploy script is maintained at c:\My Apps\hostinger\deploy-personality.ps1. It performs all three steps above in sequence:
No Firebase CLI or firebase deploy needed — the frontend is served directly by Nginx.
Backend Deploy¶
Local source is at c:\My Apps\Personality-Test\server\src\.
# SCP changed files (example — routes, middleware, and server.js)
scp "c:\My Apps\Personality-Test\server\src\routes\stripe.js" root@76.13.211.100:/opt/personality-app/src/routes/
scp "c:\My Apps\Personality-Test\server\src\middleware\rateLimiter.js" root@76.13.211.100:/opt/personality-app/src/middleware/
scp "c:\My Apps\Personality-Test\server\src\server.js" root@76.13.211.100:/opt/personality-app/src/
# Or push the full server/src at once
scp -r "c:\My Apps\Personality-Test\server\src\*" root@76.13.211.100:/opt/personality-app/src/
# Restart the app
ssh root@76.13.211.100 "pm2 restart personality-app --update-env && pm2 save"
Verify:
VPS Environment Variables¶
File: /opt/personality-app/.env
PORT=5002
MONGO_URI=mongodb://localhost:27017/personality_db
NODE_ENV=production
CORS_ORIGINS=https://mind-meditate.com,https://www.mind-meditate.com,http://localhost:3000
APP_URL=https://mind-meditate.com
# JWT Auth (VPS-issued tokens — no Firebase)
JWT_SECRET=<64-byte random secret> # generate: node -e "require('crypto').randomBytes(64).toString('hex')"
# Stripe backend
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_EXPLORER_PRICE_ID=price_...
STRIPE_COACH_FIRST_PRICE_ID=price_...
STRIPE_COACH_RENEWAL_PRICE_ID=price_...
# SMTP (password reset emails)
SMTP_HOST=smtp.hostinger.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=noreply@mind-meditate.com
SMTP_PASS="your-password" # MUST be quoted if password contains #
# Agents integration (internal — shared secret with agents-service)
INTERNAL_API_KEY=<shared-secret>
AGENTS_SERVICE_URL=http://127.0.0.1:5003
# Gemini AI
GEMINI_API_KEY=...
Important: Values containing
#must be quoted in dotenv (e.g.SMTP_PASS="abc#123"). Without quotes dotenv treats everything after#as a comment, silently truncating the value.
Nginx Config¶
File: /etc/nginx/sites-enabled/mind-meditate
Key sections:
root /opt/personality-app/public;— serves Vite build outputlocation /api/— proxies to Express port 5002location /health— proxies to Express port 5002location /—try_files $uri $uri/ /index.htmlfor SPA routinglocation /assets/— 1-year immutable cache
Reload Nginx after config changes:
Utility Scripts¶
/opt/personality-app/make-admin.js — one-off script to grant admin access to a user by email. Run when a new platform administrator needs to be set up:
The local copy lives at c:\My Apps\Personality-Test\server\scripts\make-admin.js.
PM2 Commands¶
pm2 list # show all apps
pm2 logs personality-app --lines 50 # tail logs
pm2 restart personality-app --update-env # restart with fresh env
pm2 save # persist current process list
Service Worker / PWA¶
public/sw.js and public/manifest.json provide PWA capabilities:
- App can be installed on mobile home screens.
- Offline shell caching for faster repeat loads.
Post-Deployment Checklist¶
- [ ] Verify
https://mind-meditate.com/healthreturns{"status":"ok"} - [ ] Confirm asset count: after deploy, run
ssh root@76.13.211.100 "find /opt/personality-app/public/assets -type f | wc -l"— expect ~100 files; thousands means the old-asset cleanup was skipped - [ ] Set all
STRIPE_*env vars in/opt/personality-app/.env(replace any placeholder values) - [ ] Set
INTERNAL_API_KEYto the same value as in/opt/agents-service/.env(enables agent commissions) - [ ] Register Stripe webhook endpoint in the Stripe Dashboard:
https://mind-meditate.com/api/stripe/webhook - Subscribe to:
checkout.session.completed,invoice.payment_succeeded,customer.subscription.deleted - Copy the signing secret into
STRIPE_WEBHOOK_SECRETin VPS.env - [ ] Test Explorer payment flow end-to-end on the live URL
- [ ] Test Coach payment flow: pre-approve → pay → confirm
payment_receivedstate in portal → admin activate - [ ] Test PDF export on the deployed URL
- [ ] Test promo code creation in AdminDashboard and redemption on Pricing page
- [ ] Confirm annual renewal banner appears correctly in Coach Portal