Skip to content

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/

A PowerShell deploy script is maintained at c:\My Apps\hostinger\deploy-personality.ps1. It performs all three steps above in sequence:

& "c:\My Apps\hostinger\deploy-personality.ps1"

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:

curl https://mind-meditate.com/health
# → {"status":"ok","service":"personality-app","ts":"..."}

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.

pm2 restart personality-app --update-env && pm2 save

Nginx Config

File: /etc/nginx/sites-enabled/mind-meditate

Key sections:

  • root /opt/personality-app/public; — serves Vite build output
  • location /api/ — proxies to Express port 5002
  • location /health — proxies to Express port 5002
  • location /try_files $uri $uri/ /index.html for SPA routing
  • location /assets/ — 1-year immutable cache

Reload Nginx after config changes:

nginx -t && systemctl reload nginx

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:

cd /opt/personality-app && node make-admin.js <email>

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/health returns {"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_KEY to 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_SECRET in VPS .env
  • [ ] Test Explorer payment flow end-to-end on the live URL
  • [ ] Test Coach payment flow: pre-approve → pay → confirm payment_received state 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