Deployment Plan — Market Pulse (Shared Access)¶
Target architecture: Supabase (PostgreSQL) + Railway (FastAPI + React) + Laptop (scheduler/jobs)
Status¶
| Phase | Status |
|---|---|
| Supabase provisioning | ✅ Done — market-pulse project, Northeast Asia (Tokyo) |
| Data migration SQLite → Supabase | ✅ Done — all tables migrated |
| Scheduler pointing to Supabase | ✅ Done — DATABASE_URL set in .env |
API /api prefix + React static serving |
✅ Done — Dockerfile ready |
| Railway deploy (API + frontend) | ⏳ Blocked — needs Docker Desktop (VPN/Tailscale setup pending) |
Architecture¶
Supabase (PostgreSQL, ap-northeast-1)
↑ ↑
Laptop scheduler Railway (FastAPI + React static)
(writes data) (serves dashboard to users)
The scheduler runs locally on the laptop and writes to Supabase continuously. The API and React frontend will be deployed to Railway and read from the same Supabase instance. If the laptop is off, data stops updating but the dashboard stays accessible showing the last known state.
Phase 1 — Supabase ✅¶
Project: market-pulse, region: Northeast Asia (Tokyo), compute: Nano (free).
Connection uses the Session Pooler URL (IPv4-compatible) on port 5432:
postgresql://postgres.[ref]:[password]@aws-1-ap-northeast-1.pooler.supabase.com:5432/postgres?sslmode=require
sslmode=require is set — traffic is encrypted in transit, safe on public networks.
Phase 2 — Data Migration ✅¶
Migration script copies all tables from local SQLite to Supabase:
python scripts/migrate_to_postgres.py
Tables migrated (in dependency order): users, user_sessions, stock_prices, news_articles, sentiment_scores, predictions, trending_topics.
Safe to re-run — uses ON CONFLICT DO NOTHING so existing rows are skipped.
psycopg2-binary is already in requirements.txt.
Phase 3 — Deploy to Railway ⏳¶
Blocked on Docker Desktop working with current VPN/Tailscale setup.
Prerequisites¶
- Docker Desktop installed and running
- Railway account (railway.app)
- Railway CLI:
npm install -g @railway/cli(or use the web UI)
What to deploy¶
The Dockerfile at project root is a multi-stage build:
1. Node 20 builds the React frontend (frontend/dist/)
2. Python 3.11 runs uvicorn; React dist/ is copied in and served as static files
3. Single container, single port (8080)
FastAPI serves both the API (at /api/...) and the React SPA (catch-all to index.html).
Deploy via Railway web UI¶
- Go to railway.app → New project → Deploy from GitHub repo
- Select the repo
- Railway auto-detects the Dockerfile
- Set environment variables in Railway dashboard:
DATABASE_URL=postgresql://postgres.[ref]:[password]@aws-1-ap-northeast-1.pooler.supabase.com:5432/postgres?sslmode=require FINNHUB_API_KEY=... AZURE_TRANSLATOR_KEY=... AZURE_TRANSLATOR_REGION=eastus PORT=8080 - Deploy — Railway gives a public URL like
https://market-pulse-production.up.railway.app
Redeploy after code changes¶
Push to main — Railway auto-deploys on every push if GitHub integration is enabled.
Local vs production DB¶
Once Railway is live, the intended workflow is:
- Local dev — omit
DATABASE_URLfrom.env(or point it at a local Postgres). The app falls back tosqlite:///fintech.db, giving a sandboxed dev database that never touches Supabase. - Production (Railway) —
DATABASE_URLis set as an environment secret in the Railway dashboard (see step 4 above). No.envfile on the server. - Laptop scheduler — keeps
DATABASE_URLset in.envso it continues writing live data to Supabase.
This means local changes are always tested against a throwaway SQLite DB, and only the scheduler and Railway touch the shared Supabase instance.
Phase 4 — Keep Scheduler Running on Laptop¶
No changes needed — the scheduler already writes to Supabase via DATABASE_URL in .env.
make up # starts scheduler + frontend dev server
python main.py --skip-initial # scheduler only, skip initial data reload
Cost Summary¶
| Service | Free tier | When you'd pay |
|---|---|---|
| Supabase | 500 MB PostgreSQL | If DB exceeds 500 MB (currently ~7 MB) |
| Railway | $5 credit/month | If usage exceeds credit (unlikely for small team) |
| Laptop scheduler | $0 | Electricity only |
Expected monthly cost: $0–$5