Skip to content

Prepare a Render free-tier deployment fallback for the Heroku backend #9

Description

@AlaskanTuna

The backend runs on Heroku against expiring student credit, and solarsim.tech DNS points at Heroku (apex ALIAS + www CNAME to herokudns.com), with the Heroku dyno also serving the built SPA — Vercel hosts only services/pdf-service. A ready fallback matters, but Render's free tier is materially weaker than Heroku Basic and the plan must be honest about it rather than assume parity. Verified current terms: 512 MB RAM / 0.1 CPU, 750 free instance-hours per workspace per month, 100 GB egress, 500 build minutes, spin-down after 15 minutes idle with a 30–60 s cold start, no persistent disks, and no free-tier pre-deploy command (so the release: pnpm db:migrate:deploy step in the Procfile has no direct equivalent). Render's own free Postgres is disqualified — it expires 30 days after creation — which is why task 5 puts the database on Neon regardless of where compute runs.

The two real risks, stated up front: (a) 0.1 CPU against a pipeline that downloads multi-MB GeoTIFFs and runs sharp — Phase 11 §3 already documents OOM risk at 512 MB with a full Heroku CPU share, and Phase 11 §13 already flags that the frontend's 120 s PROCESSING_TIMEOUT_MS is tight; on a tenth of a CPU, new-location resolution will likely exceed it. (b) Cold start — a 30–60 s wait on the first click is a poor first impression for a link posted to LinkedIn. Task 11 is the mitigation for both.

Scope: render.yaml (new), Procfile, package.json, .node-version (new), RUNBOOK.md, .github/workflows/ci-cd.yml

Depends on: #4, #5, #6, #7

Checklist

  • Re-verify Render's current free-tier terms at implementation time; they changed as recently as Sept 2025 (spin-down moved 30 min → 15 min)
  • Verify custom-domain support and free managed TLS on Render's free instance type before committing — if the apex cannot be served for free, the fallback is a *.onrender.com demo URL, not a solarsim.tech cutover, and that changes the plan
  • Add .node-version pinned to the same major as engines.node (24) — Render reads it, and the repo currently has no Node pin file at all
  • Add a render.yaml blueprint: one free web service, buildCommand: pnpm install --frozen-lockfile && pnpm build, startCommand: pnpm start, health check path /api/health
  • Solve migrations without a pre-deploy hook: either append pnpm db:migrate:deploy to the build command (runs before the new instance goes live) or run it manually from a local shell against Neon and document the choice in RUNBOOK.md. Note the trade-off — a build-command migration runs on every deploy including failed ones
  • Deploy to Render as a parallel environment first; do not touch DNS until it is proven
  • Measure and record in the issue: cold-start time, RSS at idle, RSS while resolving a new location, and wall-clock time for one full Solar API pipeline run
  • Compare that pipeline time against the frontend's PROCESSING_TIMEOUT_MS; if it exceeds the ceiling, note that Phase 11 §13 becomes a prerequisite for a Render cutover rather than a nice-to-have
  • Do not add a keep-alive pinger that hits a database-touching route — per task 4, holding Neon's compute awake 24/7 costs ~180 CU-hours against a 100 CU-hour free budget and will suspend the database. If a pinger is used at all, point it at a route that touches neither Postgres nor the Solar API
  • Add the Render deploy as a second, non-default job or a documented manual step in .github/workflows/ci-cd.yml; keep Heroku as the active target until the credit actually runs out
  • Write a RUNBOOK.md cutover section: DNS records to change at Porkbun, env vars to set on Render, the APEX_DOMAIN / FRONTEND_URL / SITE_URL values, and the rollback path back to Heroku
  • verify: the Render deployment serves the SPA, GET /api/health returns ok, sign-in works, and a cached project loads end-to-end
  • verify: PDF export works from the Render origin — ALLOWED_FRONTEND_ORIGIN on the Vercel pdf-service must include the new origin or the export silently 403s (this exact regression already happened once, 26/04/26)
  • verify: the measured numbers are written into the issue so the Heroku-vs-Render decision is made on data, not vibes

Priority: High · Phase 12 task 10 of the maintainer's migration plan.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1-highBlocks launch or the demoinfraHosting, DNS, CI/CD

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions