You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Goal
Move the backend (api + the pool-topup and reminders crons) off Railway onto an Oracle Cloud “Always Free” VM — free forever, and more capable than Railway’s small instance (Ampere A1 ARM: up to 4 cores / 24 GB RAM). Removes the Railway trial-credit time bomb and the ~$5/mo Hobby cost.
Why now / timing
The Railway workspace is on trial credit (was ~$4.72 / 22 days left) — when it runs out the backend stops. Stopgap: put Railway on **Hobby ($5/mo)** to avoid downtime, then do this migration on your own schedule. This is intentionally not urgent, but the credit deadline is real.
Prerequisite (do this first regardless of host): a custom API domain
The mobile app bakes EXPO_PUBLIC_API_BASE_URL (currently api-production-f3a9f.up.railway.app) into the OTA bundle, and it’s also referenced by the README APK link, the Supabase Auth redirect URL (/reset-password), and the /confirmed page. Changing the raw Railway/Oracle hostname would force a new mobile release and Supabase reconfig.
Point a custom domain (e.g. api.oneconcept.app) at the backend now, ship one mobile release that uses it, and update the Supabase redirect allowlist. After that, moving hosts is just a DNS change — no app release required. This de-risks the whole migration.
Scope — the migration chain
Provision the Oracle Always Free VM (Ampere A1, ARM64), Ubuntu.
Run the FastAPI app: the repo is already Dockerized (backend/Dockerfile) — build arm64 images; run via Docker + systemd (or docker compose) with restart-on-failure.
Crons: run pool-topup (daily 22:00 UTC) and reminders (every 15 min) via system crontab/systemd timers — or refactor them into authed HTTP endpoints and schedule via GitHub Actions/cron-job.org (decouples crons from the host; see note).
TLS + reverse proxy: Caddy (automatic Let’s Encrypt) or nginx+certbot in front of uvicorn, serving the custom domain.
Secrets: move the Railway variable store to a locked-down .env on the VM (Supabase URL/keys, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, GEMINI_API_KEY, FCM service-account, etc.).
Deploy mechanism: Railway auto-deploys from main; on a VM you lose that — add a deploy path (GitHub Actions SSH git pull && restart, or a webhook). Update RELEASING.md (“Railway auto-deploys the api from main”) accordingly.
Keep the /health keepalive (Supabase free pauses after ~7 days idle).
DNS cutover: point the custom domain at the VM, verify, then decommission Railway.
Risks / caveats
ARM64: build images for linux/arm64; check all Python wheels (asyncpg, etc.) have ARM builds (they do).
Oracle reclaims idle Always-Free instances — keep it genuinely active (the app + crons do) to avoid reclamation.
Card required at signup (Always Free itself doesn’t bill).
What this does NOT fix (separate issues): the email bottleneck (#152, still Supabase), the DB connection warmth (#149 — though a persistent VM process helps), unbounded /me/state (#150). The DB stays on Supabase.
Acceptance
Backend (api + both crons) runs on the Oracle VM behind the custom domain over HTTPS; /health green; reminders fire on schedule; a release still deploys cleanly (via the new deploy path); Railway decommissioned. No mobile release needed for the cutover (thanks to the custom domain).
Goal
Move the backend (
api+ thepool-topupandreminderscrons) off Railway onto an Oracle Cloud “Always Free” VM — free forever, and more capable than Railway’s small instance (Ampere A1 ARM: up to 4 cores / 24 GB RAM). Removes the Railway trial-credit time bomb and the ~$5/mo Hobby cost.Why now / timing
22 days left) — when it runs out the backend stops. Stopgap: put Railway on **Hobby ($5/mo)** to avoid downtime, then do this migration on your own schedule. This is intentionally not urgent, but the credit deadline is real.Prerequisite (do this first regardless of host): a custom API domain
EXPO_PUBLIC_API_BASE_URL(currentlyapi-production-f3a9f.up.railway.app) into the OTA bundle, and it’s also referenced by the README APK link, the Supabase Auth redirect URL (/reset-password), and the/confirmedpage. Changing the raw Railway/Oracle hostname would force a new mobile release and Supabase reconfig.api.oneconcept.app) at the backend now, ship one mobile release that uses it, and update the Supabase redirect allowlist. After that, moving hosts is just a DNS change — no app release required. This de-risks the whole migration.Scope — the migration chain
backend/Dockerfile) — build arm64 images; run via Docker +systemd(or docker compose) with restart-on-failure.pool-topup(daily 22:00 UTC) andreminders(every 15 min) via systemcrontab/systemd timers — or refactor them into authed HTTP endpoints and schedule via GitHub Actions/cron-job.org (decouples crons from the host; see note)..envon the VM (Supabase URL/keys,SUPABASE_ANON_KEY,SUPABASE_SERVICE_ROLE_KEY,GEMINI_API_KEY, FCM service-account, etc.).main; on a VM you lose that — add a deploy path (GitHub Actions SSHgit pull && restart, or a webhook). UpdateRELEASING.md(“Railway auto-deploys the api from main”) accordingly./healthkeepalive (Supabase free pauses after ~7 days idle).Risks / caveats
linux/arm64; check all Python wheels (asyncpg, etc.) have ARM builds (they do).What this does NOT fix (separate issues): the email bottleneck (#152, still Supabase), the DB connection warmth (#149 — though a persistent VM process helps), unbounded
/me/state(#150). The DB stays on Supabase.Acceptance
/healthgreen; reminders fire on schedule; a release still deploys cleanly (via the new deploy path); Railway decommissioned. No mobile release needed for the cutover (thanks to the custom domain).