Skip to content

Infrastructure

Regulus uses a composable, cloud-agnostic infrastructure designed to run at ~$5–30/month during Alpha and scale to $500/month at 50k MAU without architecture changes.


Service Map

ServiceProviderPurposeNotes
APISingle VPS (regulus, 204.168.160.114)NestJS modular monolith under PM2nginx → api.rgls.uk
Real-timeSame VPSCentrifugo self-hostedws.rgls.uk
DatabasePostgres on same VPS (Docker, port 5433)Primary DB + pgvectorLocal, no PgBouncer yet
AuthCustom JWT via NestJSInvite-only register flowIssued by apps/api, no Supabase Auth
StorageCloudflare R2Media uploadsZero egress for video
Admin panelSame VPS, nginx-served Vite SPA/var/www/regulus-app/admin/distadmin.rgls.uk. No Vercel.
MobileExpo EASiOS + Android OTA updatesEAS Build + EAS Submit
PushExpo Push APIMobile push notifications600 notif/sec capacity
EmailResend (or SMTP)Transactional emailWelcome, invite-used, digest
ObservabilityPostHog + SentryAnalytics + error trackingFree tiers

API — VPS + PM2

Repository: apps/api/Host: regulus (204.168.160.114), root, SSH key in ~/.ssh/regulus_deploy.

Layout on the box:

/opt/regulus-app/
  repo/                        # rsync target (full monorepo, no .git)
    apps/api/dist/             # compiled NestJS bundle (entry: dist/main.js)
    apps/api/prisma/migrations # for `prisma migrate deploy`
    node_modules/              # installed once, reused across deploys
  ecosystem.config.cjs         # PM2 spec for regulus-api
  logs/                        # api.out.log, api.err.log
  admin-dist/                  # admin bundle, kept as a stage copy

Deploy (from your laptop):

bash
# 1. Build everything locally
pnpm build

# 2. Sync compiled bundle + prisma migrations + domain dist
rsync -avz --delete apps/api/dist/      regulus:/opt/regulus-app/repo/apps/api/dist/
rsync -avz --delete apps/api/prisma/    regulus:/opt/regulus-app/repo/apps/api/prisma/ --exclude='migration_lock.toml'
rsync -avz --delete packages/domain/dist/ regulus:/opt/regulus-app/repo/packages/domain/dist/

# 3. Migrate + regenerate Prisma client + restart on server
#    NOTE: `prisma generate` is mandatory whenever schema.prisma changed —
#    otherwise the existing @prisma/client in node_modules will reject any
#    new fields ("Unknown field X on UserCountOutputType" / 500).
ssh regulus 'cd /opt/regulus-app/repo/apps/api \
  && pnpm prisma migrate deploy \
  && pnpm prisma generate \
  && pm2 restart regulus-api'

Health check: GET /health{ status: "ok", uptime, version } (outside /api/v1 prefix). Public: https://api.rgls.uk/health. PM2 binds the process to localhost:4101; nginx terminates TLS.


Centrifugo — same VPS

Self-hosted real-time server. Users subscribe to:

  • user:{userId} — personal notifications channel
  • feed:public — new public post IDs
  • conversation:{conversationId} — message delivery (planned)

Runs as a separate PM2 / systemd unit; nginx vhost regulus-app-ws proxies wss://ws.rgls.uk/connection/websocket. Config via env — see Environment.


Supabase

Project: regulus-alpha

Database

  • Postgres 15 with pgvector extension (for future AI embeddings).
  • Row-Level Security (RLS) policies on public-facing tables.
  • Connection via DATABASE_URL (pooled PgBouncer URL for API, direct for migrations).

Storage

Two buckets:

  • media — user-uploaded photos/videos (private, presigned URLs).
  • avatars — profile pictures (public CDN).

Upload flow:

  1. Client requests presigned PUT URL: POST /media/upload{ uploadUrl, key }.
  2. Client uploads directly to R2/Supabase Storage.
  3. Client commits: POST /media/commit { key } → creates MediaAsset row.

Cloudflare R2

Used for large media (videos). Zero egress fees. Served via Cloudflare CDN.

  • Bucket: regulus-media
  • CORS: allow rgls.uk + *.regulus.app
  • Lifecycle: delete pending media assets older than 24h (server cron).

Admin Panel — same VPS, nginx-served Vite SPA

Repository: apps/admin/ (Vite + React 19 + Ant Design — not Next.js). Public URL: https://admin.rgls.ukPath on server: /var/www/regulus-app/admin/dist

API base URL is hard-coded to https://api.rgls.uk in apps/admin/src/lib/api.ts (no build-time env needed for prod).

Deploy (from your laptop):

bash
pnpm --filter @regulus/admin build
rsync -avz --delete apps/admin/dist/ regulus:/opt/regulus-app/admin-dist/
ssh regulus 'rsync -a --delete /opt/regulus-app/admin-dist/ /var/www/regulus-app/admin/dist/'

No restart needed — nginx (/etc/nginx/sites-enabled/regulus-app-admin) serves the static SPA directly. Cloudflare sits in front of nginx for TLS + real-IP.


Expo EAS — Mobile

Repository: apps/mobile/

bash
eas build --platform all --profile production
eas submit --platform all
eas update --branch production --message "v1.2.3"

OTA updates via expo-updates. New JS bundle pushed without App Store review.


Cost Breakdown (Alpha)

ServiceMonthly Cost
VPS (API + admin + Centrifugo + Postgres)~$10–25
Cloudflare R2~$0 (free 10 GB)
Cloudflare DNS/TLS$0
Expo EAS$0 (free tier)
Resend (email)$0–5
PostHog + Sentry$0 (free tiers)
Total~$5–40

Regulus — invite-only social-knowledge platform