Deployment
Deployment
Section titled “Deployment”ClinicFlow runs on Railway (backend + frontend), with Supabase for the database, the Railway Redis plugin for queues and caching, and Cloudflare for DNS/SSL.
Architecture
Section titled “Architecture”clinicflow.lat (Cloudflare DNS) ├── app.clinicflow.lat → Railway (Frontend static service) ├── api.clinicflow.lat → Railway (Backend Node.js service) └── Email routing → Gmail (support@, sales@, info@, billing@)Railway Setup
Section titled “Railway Setup”Backend Service
Section titled “Backend Service”- Create a new Railway project
- Add a service → Deploy from GitHub repo → select
clinic-saas-production - Set Root Directory to
backend - Start command:
node src/index.js(or Railway auto-detects frompackage.jsonstartscript) - Add all environment variables from Environment Variables
- Set a custom domain:
api.clinicflow.lat
Frontend Service
Section titled “Frontend Service”- Add another service in the same Railway project
- Deploy from same repo, Root Directory →
frontend - Build command:
npm run build - Start command:
caddy run --config Caddyfile(or Railway static site detection) - Environment variables:
VITE_API_URL=https://api.clinicflow.lat/apiVITE_LS_URL_BASIC=https://...VITE_LS_URL_PRO=https://...VITE_LS_URL_CLINICA=https://...
- Set custom domain:
app.clinicflow.lat
The Caddyfile handles SPA routing (all paths → index.html):
:80 { root * /app/dist try_files {path} {path}/ /index.html file_server}Supabase
Section titled “Supabase”Production Project Setup
Section titled “Production Project Setup”- Create a new Supabase project at supabase.com
- Go to SQL Editor → paste and run
backend/src/db/schema.sql - Enable Row Level Security on all tables (the schema file does this)
- Copy credentials: Settings → API → URL, anon key, service role key
Migrations
Section titled “Migrations”After the initial schema is applied, deploy migrations:
cd backendnpm run migrateBackups
Section titled “Backups”Supabase Pro plan includes daily backups. For critical data (appointments, patients), set up point-in-time recovery (PITR) if on the Pro plan.
Railway provides a managed Redis plugin. Add it from the Railway dashboard:
- Open your project → New → Plugin → Redis
- Copy the connection string — it becomes
REDIS_URLautomatically in Railway environment variables
For local development, use any Redis instance (local Docker, Railway fork, or a hosted provider).
Cloudflare DNS
Section titled “Cloudflare DNS”Point app and api subdomains to Railway:
Type Name Content ProxyCNAME app your-frontend.up.railway.app ✓ (orange cloud)CNAME api your-backend.up.railway.app ✓ (orange cloud)SSL is handled by Cloudflare’s edge. Railway also provides SSL termination. Both work.
Email routing (for support@, sales@, info@, billing@):
In Cloudflare → Email → Email Routing:
- Create catch-all rule forwarding to your Gmail address
- Or add individual rules per address
LemonSqueezy
Section titled “LemonSqueezy”- Create a store at lemonsqueezy.com
- Create products for each plan (Basic, Pro, Clínica)
- For each product, create a variant and copy the Variant ID →
LS_VARIANT_*env vars - In Store → Webhooks: add a webhook pointing to
https://api.clinicflow.lat/webhooks/lemonsqueezy- Events:
order_created - Copy the Signing Secret →
LEMONSQUEEZY_SIGNING_SECRET
- Events:
Sentry
Section titled “Sentry”- Create a project at sentry.io (choose Node.js for backend, React for frontend)
- Copy the DSN values →
SENTRY_DSN(backend),VITE_SENTRY_DSN(frontend) - Sentry is initialized in
backend/src/instrument.jsandfrontend/src/main.jsx
Meta / WhatsApp Cloud API
Section titled “Meta / WhatsApp Cloud API”The webhook for all clinics’ WhatsApp messages routes to:
https://api.clinicflow.lat/webhooks/whatsappConfigure in your Meta App → WhatsApp → Configuration:
- Webhook URL:
https://api.clinicflow.lat/webhooks/whatsapp - Verify Token: value of
WHATSAPP_WEBHOOK_VERIFY_TOKEN - Subscribed fields:
messages
Each clinic registers their own phone number through the Settings tab in the dashboard (see WhatsApp Setup Guide).
Deploy Checklist
Section titled “Deploy Checklist”Before going live with a new environment:
- Schema applied to Supabase (
schema.sqlrun in SQL Editor) - All required env vars set in Railway
-
ENCRYPTION_KEYis exactly 32 characters -
JWT_SECRETis at least 32 characters - Meta webhook URL configured and verified
- LemonSqueezy webhook URL configured
- Sentry DSN set (or left blank to disable)
- Custom domains configured in Railway + Cloudflare
- First superadmin invite code created via
POST /api/superadmin/invite-codes - Health check passes:
curl https://api.clinicflow.lat/health