From a3b36b52324b7b908df9c3a717da596e9b98fdc4 Mon Sep 17 00:00:00 2001 From: R4ph-t <1368445+R4ph-t@users.noreply.github.com> Date: Thu, 26 Feb 2026 06:29:57 +0000 Subject: [PATCH] Sync skills from render-oss/skills --- .../render-migrate-from-heroku/SKILL.md | 312 ++++++++++++++++++ .../references/blueprint-example.md | 181 ++++++++++ .../references/buildpack-mapping.md | 164 +++++++++ .../references/data-migration.md | 201 +++++++++++ .../references/mcp-setup.md | 102 ++++++ .../references/preflight-checklist.md | 69 ++++ .../references/service-mapping.md | 169 ++++++++++ 7 files changed, 1198 insertions(+) create mode 100644 plugins/render/skills/render-migrate-from-heroku/SKILL.md create mode 100644 plugins/render/skills/render-migrate-from-heroku/references/blueprint-example.md create mode 100644 plugins/render/skills/render-migrate-from-heroku/references/buildpack-mapping.md create mode 100644 plugins/render/skills/render-migrate-from-heroku/references/data-migration.md create mode 100644 plugins/render/skills/render-migrate-from-heroku/references/mcp-setup.md create mode 100644 plugins/render/skills/render-migrate-from-heroku/references/preflight-checklist.md create mode 100644 plugins/render/skills/render-migrate-from-heroku/references/service-mapping.md diff --git a/plugins/render/skills/render-migrate-from-heroku/SKILL.md b/plugins/render/skills/render-migrate-from-heroku/SKILL.md new file mode 100644 index 0000000..db18910 --- /dev/null +++ b/plugins/render/skills/render-migrate-from-heroku/SKILL.md @@ -0,0 +1,312 @@ +--- +name: render-migrate-from-heroku +description: "Migrate from Heroku to Render by reading local project files and generating equivalent Render services. Triggers: any mention of migrating from Heroku, moving off Heroku, Heroku to Render migration, or switching from Heroku. Reads Procfile, dependency files, and app config from the local repo. Optionally uses Heroku MCP to enrich with live config vars, add-on details, and dyno sizes. Uses Render MCP or Blueprint YAML to create services." +license: MIT +compatibility: Render MCP server recommended for direct creation and automated verification; not required for the Blueprint path. Heroku MCP server is optional (enhances config var and add-on discovery). +metadata: + author: Render + version: "1.5.0" + category: migration +--- + +# Heroku to Render Migration + +Migrate from Heroku to Render by reading local project files first, then optionally enriching with live Heroku data via MCP. + +## Prerequisites Check + +Before starting, verify what's available: + +1. **Local project files** (required) — confirm the current directory contains a Heroku app (look for `Procfile`, `app.json`, `package.json`, `requirements.txt`, `Gemfile`, `go.mod`, or similar) +2. **Render MCP** (recommended) — check if `list_services` tool is available. Required for MCP Direct Creation (Step 3B) and automated verification (Step 6). Not required for the Blueprint path — the Render CLI and Dashboard handle generation, validation, and deployment. +3. **Heroku MCP** (optional) — check if `list_apps` tool is available + +If Render MCP is missing and the user needs it, guide them through setup using the [MCP setup guide](references/mcp-setup.md). If Heroku MCP is missing, note that config var values and add-on plan details will need to be provided manually. + +## Migration Workflow + +Execute steps in order. Present findings to the user and get confirmation before creating any resources. + +### Step 1: Inventory Heroku App + +Gather app details from local files first, then supplement with Heroku MCP if available. + +#### 1a. Read local project files (always) + +Read these files from the repo to determine runtime, commands, and dependencies: + +| File | What it tells you | +| ------------------------------------------------- | ---------------------------------------------------------------------- | +| `Procfile` | Process types and start commands (`web`, `worker`, `clock`, `release`) | +| `package.json` | Node.js runtime, build scripts, framework deps (Next.js, React, etc.) | +| `requirements.txt` / `Pipfile` / `pyproject.toml` | Python runtime, dependencies (Django, Flask, etc.) | +| `Gemfile` | Ruby runtime, dependencies (Rails, Sidekiq, etc.) | +| `go.mod` | Go runtime | +| `Cargo.toml` | Rust runtime | +| `app.json` | Declared add-ons, env var descriptions, buildpacks | +| `runtime.txt` | Pinned runtime version | +| `static.json` | Static site indicator | +| `yarn.lock` / `pnpm-lock.yaml` | Package manager (affects build command) | + +From these files, determine: + +- **Runtime** — from dependency files (see the [buildpack mapping](references/buildpack-mapping.md)) +- **Runtime version** — from `runtime.txt`, `.node-version`, or `engines` in `package.json`. If pinned, carry it over as an env var (e.g., `PYTHON_VERSION`, `NODE_VERSION`). If not pinned, do not specify a version — never assume or state what Render's default version is. +- **Build command** — from package manager and framework (see the [buildpack mapping](references/buildpack-mapping.md)) +- **Start commands** — from `Procfile` entries +- **Process types** — from `Procfile` (web, worker, clock, release) +- **Add-ons needed** — from `app.json` `addons` field, or infer from dependency files (e.g., `pg` in `package.json` suggests Postgres, `redis` suggests Key Value) +- **Static site?** — from `static.json`, SPA framework deps, or static buildpack in `app.json` + +#### 1b. Enrich with Heroku MCP (if available) + +If the Heroku MCP server is connected, call these tools to fill in details that aren't in the repo. The **dyno size** and **add-on plan slug** are critical — they determine which Render plans to use. + +1. `list_apps` — let user select which app to migrate (confirms app name) +2. `get_app_info` — capture: region, stack, buildpacks, **config var names** +3. `list_addons` — capture the **exact add-on plan slug** (e.g., `heroku-postgresql:essential-2`, `heroku-redis:premium-0`). The part after the colon maps to a specific Render plan in the [service mapping](references/service-mapping.md). +4. `ps_list` — capture the **exact dyno size** for each process type (e.g., `Standard-2X`, `Performance-M`). Each dyno size maps to a specific Render plan in the [service mapping](references/service-mapping.md). +5. `pg_info` (if Postgres exists) — capture **Data Size** (actual usage) and the plan's disk allocation. The plan's disk size determines the `diskSizeGB` value in the Blueprint (see the [service mapping](references/service-mapping.md)). + +If Heroku MCP is **not** available, ask the user to provide: + +- Dyno sizes (or run `heroku ps:type -a ` and paste output) +- Add-on plans (or run `heroku addons -a ` and paste output) +- Database info (or run `heroku pg:info -a ` and paste output — captures plan name, data size, and disk allocation) +- App region (`us` or `eu`) +- Config var names (or run `heroku config -a --shell` and paste output) + +If the user cannot provide dyno sizes or add-on plans, use the fallback defaults from the [service mapping](references/service-mapping.md): `starter` for compute, `basic-1gb` for Postgres, `starter` for Key Value. + +#### Present summary + +``` +App: [name] | Region: [region] | Runtime: [node/python/ruby/etc] +Source: [local files | local files + Heroku MCP] +Build command: [inferred from buildpack/deps] +Processes: + web: [command from Procfile] → Render web service ([mapped-plan]) + worker: [command] → Render background worker ([mapped-plan], Blueprint only) + clock: [command] → Render cron job ([mapped-plan]) + release: [command] → Append to build command +Add-ons: + Heroku Postgres ([plan-slug], [disk-size]) → Render Postgres ([mapped-plan], diskSizeGB: [size]) + Heroku Redis ([plan-slug]) → Render Key Value ([mapped-plan]) +Config vars: 14 total (list names, not values) +``` + +### Step 2: Pre-Flight Check + +Before creating anything, run through the [pre-flight checklist](references/preflight-checklist.md) to validate the migration plan. Key checks: + +- Runtime supported (or needs Dockerfile) +- Worker dynos, release phase, static site detection +- Third-party add-ons without Render equivalents +- Git remote exists and is HTTPS format +- Database size (large DBs need assisted migration) + +Look up each Heroku dyno size and add-on plan in the [service mapping](references/service-mapping.md) to determine correct Render plans and cost estimates. Present the migration plan table from the [pre-flight checklist](references/preflight-checklist.md) and wait for user confirmation before creating any resources. + +### Determine Creation Method + +After the user approves the pre-flight plan, apply this decision rule. **Default to Blueprint** — only use MCP Direct Creation when every condition below is met. + +**Use Blueprint** (the default) when ANY are true: + +- Multiple process types (web + worker, web + cron, etc.) +- Databases or Key Value stores needed +- Background workers in the Procfile +- User prefers Infrastructure-as-Code configuration + +**Fall back to MCP Direct Creation** ONLY when ALL are true: + +- Single web or static site service (one process type) +- No background workers or cron jobs +- No databases or Key Value stores + +If unsure, use Blueprint. Most Heroku apps have at least a database, so Blueprint applies to the vast majority of migrations. + +### Step 3A: Generate Blueprint (Multi-Service) + +This step has three mandatory sub-steps. Complete all three in order. + +#### 3A-i. Write render.yaml + +Generate a `render.yaml` file and write it to the repo root. See the [Blueprint example](references/blueprint-example.md) for a complete example, the [Blueprint docs](https://render.com/docs/blueprint-spec#projects-and-environments) for usage guidance, and the [Blueprint YAML JSON schema](https://render.com/schema/render.yaml.json) for the full field reference. + +**IMPORTANT: Always use the `projects`/`environments` pattern.** The YAML must start with a `projects:` key — never use flat top-level `services:` or `databases:` keys. This groups all migrated resources into a single Render project. + +**Set the `plan:` field for each service and database using the mapped Render plan from the [service mapping](references/service-mapping.md).** Look up the Heroku dyno size (from `ps_list`) and add-on plan slug (from `list_addons`) to find the correct Render plan. If the Heroku plan is unknown, use the fallback defaults: `starter` for compute, `basic-1gb` for Postgres, `starter` for Key Value. + +Generate the YAML following the full template, rules, and patterns in the [Blueprint example](references/blueprint-example.md). Critical rules: + +- Always use the `projects:`/`environments:` pattern — never flat top-level `services:` +- Set every `plan:` field using the [service mapping](references/service-mapping.md) +- Set `diskSizeGB` on databases from the Heroku disk allocation +- Use `fromDatabase` for `DATABASE_URL` and `fromService` for `REDIS_URL` — never hardcode connection strings +- Mark secrets with `sync: false` + +#### 3A-ii. Validate the Blueprint + +This step is mandatory. First, check if the Render CLI is installed: + +```bash +render --version +``` + +If not installed, offer to install it: + +- macOS: `brew install render` +- Linux/macOS: `curl -fsSL https://raw.githubusercontent.com/render-oss/cli/main/bin/install.sh | sh` + +Once the CLI is available, run the validation command and show the output to the user: + +```bash +render blueprints validate render.yaml +``` + +If validation fails, fix the errors in the YAML and re-validate. Repeat until validation passes. **Do not proceed to the next step until the Blueprint validates successfully.** + +#### 3A-iii. Provide the deploy URL + +After validation passes: + +1. Instruct user to commit and push: `git add render.yaml && git commit -m "Add Render migration Blueprint" && git push` +2. Get the repo URL by running `git remote get-url origin`. If the URL is SSH format (e.g., `git@github.com:user/repo.git`), convert it to HTTPS (`https://github.com/user/repo`). Then construct the deeplink: `https://dashboard.render.com/blueprint/new?repo=` +3. Present the **actual working deeplink** to the user — never show a placeholder URL. Guide user to open it, fill in `sync: false` secrets, and click **Apply** + +**Do not skip the deploy URL.** The user needs this link to apply the Blueprint on Render. + +### Step 3B: MCP Direct Creation (Single-Service) + +Before creating resources via MCP, verify the active workspace: + +``` +get_selected_workspace() +``` + +If the workspace is wrong, list available workspaces with `list_workspaces()` and ask the user to select the correct one. Resources will be created in whichever workspace is active. + +For single-service migrations without databases, create via MCP tools: + +1. **Web service** — `create_web_service` with: + - `runtime`: from the [buildpack mapping](references/buildpack-mapping.md) + - `buildCommand`: from the [buildpack mapping](references/buildpack-mapping.md) + - `startCommand`: from Procfile `web:` entry + - `repo`: user-provided GitHub/GitLab URL + - `region`: mapped from Heroku region + - `plan`: mapped from Heroku dyno size using the [service mapping](references/service-mapping.md) (fallback: `starter`) +2. **Static site** — `create_static_site` if detected (instead of web service) + +Present the creation result (service URL, ID) when complete. + +### Step 4: Migrate Environment Variables + +#### Gather config vars + +Use the first available source: + +1. **Heroku MCP** (preferred) — config vars from `get_app_info` results (Step 1b) +2. **User-provided** — ask the user to paste output of `heroku config -a --shell` +3. **`app.json`** — var names and descriptions (no values, but useful for `sync: false` entries) + +#### Filter and categorize + +Remove auto-generated and Heroku-specific vars (see the full filter list in the [service mapping](references/service-mapping.md)): + +- `DATABASE_URL`, `REDIS_URL`, `REDIS_TLS_URL` (Render generates these) +- `HEROKU_*` vars (e.g., `HEROKU_APP_NAME`, `HEROKU_SLUG_COMMIT`) +- Add-on connection strings (`PAPERTRAIL_*`, `SENDGRID_*`, etc.) + +Present filtered list to user — **do not write without confirmation**. + +#### Apply vars + +**Blueprint path (Step 3A):** Env vars are already embedded in the `render.yaml` on each service (non-secret values inline, secrets marked `sync: false` for the user to fill in during Blueprint apply). No separate MCP call is needed — skip to Step 5. + +**MCP path (Step 3B):** Call Render `update_environment_variables` with confirmed vars (supports bulk set, merges by default). + +### Step 5: Data Migration + +Follow the [data migration guide](references/data-migration.md) to migrate Postgres and Redis data. The guide covers sub-steps 5a through 5e in detail. Summary of the flow: + +1. **Pre-migration checks** — confirm Render resources are provisioned via `list_postgres_instances()` and `list_key_value()`, check source DB size, verify Render CLI (`render --version`), `pg_dump`, and `pg_restore` are installed +2. **Gather connection strings** — Heroku Postgres via `pg_credentials` (MCP) or user CLI paste. For Key Value, construct a Dashboard deeplink from the ID. +3. **Postgres migration** — two approaches based on size: **under 2 GB** uses `render psql` (no Render connection string needed); **2-50 GB** uses `pg_dump -Fc` + `pg_restore` with external connection string from Dashboard (faster, compressed, parallel restore). +4. **Key Value / Redis** — usually skip (ephemeral cache). If persistent data, use `redis-cli` dump/restore with Dashboard-provided Render URL. +5. **Data validation** — verify schema and row counts via `query_render_postgres`, compare against Heroku source if MCP is available. + +### Step 6: Verify Migration + +After user confirms database migration is complete, run through each check in order. Stop at the first failure, fix it, and redeploy before continuing. + +#### 1. Confirm deploy status + +``` +list_deploys(serviceId: "", limit: 1) +``` + +Expect `status: "live"`. If status is `failed`, inspect build and runtime logs immediately. + +#### 2. Verify service health + +Hit the health endpoint (or `/`) and confirm a 200 response. If there is no health endpoint, verify the app binds to `0.0.0.0:$PORT` (not `localhost`). + +#### 3. Scan error logs + +``` +list_logs(resource: [""], level: ["error"], limit: 50) +``` + +Look for clear failure signatures: missing env vars, connection refused, module not found, port binding errors. + +#### 4. Verify env vars and port binding + +Confirm all required env vars are set — especially secrets marked `sync: false` during Blueprint apply. Ensure the app binds to `0.0.0.0:$PORT`. + +#### 5. Check resource metrics + +``` +get_metrics( + resourceId: "", + metricTypes: ["http_request_count", "cpu_usage", "memory_usage"] +) +``` + +Verify CPU and memory are within expected ranges for the selected plan. + +#### 6. Confirm database connectivity + +``` +query_render_postgres(postgresId: "", sql: "SELECT count(*) FROM ") +``` + +Run a read-only query on a key table to confirm data was restored correctly. Compare row counts against the Heroku source if possible. + +Present a health summary after all checks pass. + +### Step 7: DNS Cutover (Manual) + +Instruct user to: + +1. Add CNAME pointing domain to `[service-name].onrender.com` +2. Remove/update old Heroku DNS entries +3. Wait for propagation + +## Rollback Plan + +If the migration fails at any point: + +- **Services created but not working**: Services can be deleted from the Render dashboard (MCP server intentionally does not support deletion). Heroku app is untouched until maintenance mode is enabled. +- **Env vars wrong**: Call `update_environment_variables` with `replace: true` to overwrite, or fix individual vars. +- **Database migration failed**: Render Postgres can be deleted and recreated. Heroku database is read-only during dump (no data loss). If `maintenance_off` is called on Heroku, the original app is fully operational again. +- **DNS already changed**: Revert CNAME to Heroku and disable maintenance mode on Heroku. + +Key principle: **Heroku stays fully functional until the user explicitly cuts over DNS.** The migration is additive until that final step. + +## Error Handling + +- Service creation fails: show error, suggest fixes (invalid plan, bad repo URL) +- Env var migration partially fails: show which succeeded/failed +- Heroku auth errors: instruct `heroku login` or check `HEROKU_API_KEY` +- Render auth errors: check Render API key in MCP config diff --git a/plugins/render/skills/render-migrate-from-heroku/references/blueprint-example.md b/plugins/render/skills/render-migrate-from-heroku/references/blueprint-example.md new file mode 100644 index 0000000..fc6b993 --- /dev/null +++ b/plugins/render/skills/render-migrate-from-heroku/references/blueprint-example.md @@ -0,0 +1,181 @@ +# Blueprint Example: Heroku Migration with Project/Environment Pattern + +This example shows a complete `render.yaml` for migrating a typical Heroku app with a web dyno, worker dyno, Heroku Scheduler (clock), Postgres, and Redis. It uses the `projects`/`environments` pattern to group all resources in a single Render project. + +References: [Blueprint docs](https://render.com/docs/blueprint-spec#projects-and-environments) | [Blueprint YAML JSON schema](https://render.com/schema/render.yaml.json) + +## Full Example + +Assumes a Node.js app named `acme-app` migrating from Heroku US region. + +```yaml +previews: + generation: off +projects: + - name: acme-app + environments: + - name: production + services: + # Web service (from Heroku web dyno) + - type: web + name: acme-app-web + runtime: node + plan: starter + region: oregon + buildCommand: npm ci && npm run build + startCommand: npm start + healthCheckPath: /health + envVars: + - key: NODE_ENV + value: production + - key: DATABASE_URL + fromDatabase: + name: acme-app-db + property: connectionString + - key: REDIS_URL + fromService: + type: keyvalue + name: acme-app-cache + property: connectionString + - key: APP_NAME + value: acme-app + - key: LOG_LEVEL + value: info + - key: STRIPE_API_KEY + sync: false + - key: JWT_SECRET + sync: false + + # Background worker (from Heroku worker dyno) + - type: worker + name: acme-app-worker + runtime: node + plan: starter + region: oregon + buildCommand: npm ci + startCommand: node worker.js + envVars: + - key: DATABASE_URL + fromDatabase: + name: acme-app-db + property: connectionString + - key: REDIS_URL + fromService: + type: keyvalue + name: acme-app-cache + property: connectionString + - key: APP_NAME + value: acme-app + - key: LOG_LEVEL + value: info + - key: STRIPE_API_KEY + sync: false + + # Cron job (from Heroku Scheduler or clock dyno) + - type: cron + name: acme-app-cron + runtime: node + plan: starter + region: oregon + schedule: "0 * * * *" + buildCommand: npm ci + startCommand: node scripts/scheduled-task.js + envVars: + - key: DATABASE_URL + fromDatabase: + name: acme-app-db + property: connectionString + - key: APP_NAME + value: acme-app + - key: STRIPE_API_KEY + sync: false + + # Key Value (from Heroku Data for Redis) + - type: keyvalue + name: acme-app-cache + plan: starter + ipAllowList: + - source: 0.0.0.0/0 + description: everywhere + + databases: + # Postgres (from Heroku Postgres) + - name: acme-app-db + plan: basic-1gb + diskSizeGB: 10 # carried over from Heroku plan allocation + +``` + +## Key Patterns + +### Service references + +Use `fromDatabase` and `fromService` instead of hardcoding connection strings: + +```yaml +# Postgres connection string +- key: DATABASE_URL + fromDatabase: + name: acme-app-db + property: connectionString + +# Key Value (Redis) connection string +- key: REDIS_URL + fromService: + type: keyvalue + name: acme-app-cache + property: connectionString +``` + +### Environment variables + +Define env vars directly on each service. Do not use `envVarGroups` — they can cause misapplication issues during Blueprint sync. + +### Secrets + +Mark secrets with `sync: false` so the user is prompted in the Dashboard: + +```yaml +- key: STRIPE_API_KEY + sync: false +``` + +Render prompts for these values only during the initial Blueprint apply. For updates after initial creation, set secrets manually in the Dashboard or via MCP `update_environment_variables`. + +## Blueprint Rules + +Follow these rules when generating a `render.yaml` for migration: + +- **Always use the `projects:`/`environments:` pattern** — the YAML must start with a `projects:` key. Never use flat top-level `services:` or `databases:` keys. +- **Set every `plan:` field** using the [service mapping](service-mapping.md) — look up the Heroku dyno size or add-on plan and use the mapped Render plan. Never hardcode `starter` without checking the mapping first. +- **Set `diskSizeGB` on databases** — carry over the Heroku disk allocation from the [service mapping](service-mapping.md). Round up to 1 or the nearest multiple of 5. Render storage is expandable and can be resized later. +- Always include `previews: { generation: off }` at the root level to disable preview environments by default. +- Use `fromDatabase` for `DATABASE_URL` — never hardcode connection strings. +- Use `fromService` with `type: keyvalue` and `property: connectionString` for `REDIS_URL`. +- Define env vars directly on each service (do not use `envVarGroups`). +- Mark secrets with `sync: false` (user fills these in the Dashboard during Blueprint apply). +- Map region from Heroku using the [service mapping](service-mapping.md). +- Only include service/database blocks that the Heroku app actually uses. + +## Plan Selection + +Set every `plan:` field by looking up the Heroku dyno size or add-on plan in the [service mapping](service-mapping.md). The example above uses `starter` and `basic-1gb` as representative values. In a real migration, replace these with the mapped plan for the actual Heroku configuration. + +**Fallback defaults** (when the Heroku plan is unknown): + +| Service type | Fallback plan | +|---|---| +| Web / worker / cron / static / pserv | `starter` | +| Key Value | `starter` | +| Postgres | `basic-1gb` | + +## Adapting This Example + +- **Python app:** Change `runtime: node` to `runtime: python`, update build/start commands (e.g., `pip install -r requirements.txt`, `gunicorn app:app`) +- **Ruby app:** Change to `runtime: ruby`, update commands (e.g., `bundle install`, `bundle exec puma`) +- **No worker:** Remove the `type: worker` service block +- **No cron:** Remove the `type: cron` service block +- **No Redis:** Remove the `type: keyvalue` service block and any `REDIS_URL` env var references +- **No Postgres:** Remove the `databases` section and any `DATABASE_URL` env var references +- **Static site:** Replace `type: web` with `runtime: static` and add `staticPublishPath` +- **EU region:** Change `region: oregon` to `region: frankfurt` (maps from Heroku `eu` region) diff --git a/plugins/render/skills/render-migrate-from-heroku/references/buildpack-mapping.md b/plugins/render/skills/render-migrate-from-heroku/references/buildpack-mapping.md new file mode 100644 index 0000000..7b552e3 --- /dev/null +++ b/plugins/render/skills/render-migrate-from-heroku/references/buildpack-mapping.md @@ -0,0 +1,164 @@ +# Buildpack, Procfile, and Build Command Mapping + +## Buildpack → Render Runtime + Build Command + +Render needs explicit `buildCommand` and `startCommand`. Determine the runtime from local project files (dependency files, Procfile), or from Heroku buildpack URLs if available via MCP or `app.json`. Use these defaults, then refine based on the app's actual Procfile and package config. + +### Node.js + +| Buildpack | `heroku/nodejs` or `heroku-community/nodejs` | +|-----------|----------------------------------------------| +| Render runtime | `node` | +| Default build command | `npm install && npm run build` | +| Fallback build (no build script) | `npm install` | +| Detection | Check `package.json` for `build` script. If missing, use fallback. | + +**Common variations:** +- Yarn: `yarn install && yarn build` (detect via `yarn.lock` presence) +- pnpm: `pnpm install && pnpm run build` (detect via `pnpm-lock.yaml`) +- TypeScript without build script: `npm install && npx tsc` +- Next.js: `npm install && npm run build` (start: `npm start` or `next start`) +- Vite/CRA (static): use `create_static_site` instead, publishPath `dist` or `build` + +### Python + +| Buildpack | `heroku/python` | +|-----------|-----------------| +| Render runtime | `python` | +| Default build command | `pip install -r requirements.txt` | +| Django build | `pip install -r requirements.txt && python manage.py collectstatic --noinput` | +| Detection | Check for `requirements.txt`, `Pipfile`, or `pyproject.toml` | + +**Common variations:** +- Pipenv: `pip install pipenv && pipenv install` +- Poetry: `pip install poetry && poetry install` +- Django: look for `collectstatic` in Procfile or `django` in requirements +- FastAPI/Flask: straightforward `pip install -r requirements.txt` + +### Ruby + +| Buildpack | `heroku/ruby` | +|-----------|---------------| +| Render runtime | `ruby` | +| Default build command | `bundle install` | +| Rails build | `bundle install && bundle exec rake assets:precompile` | +| Detection | Check for `Gemfile`. If `rails` gem present, use Rails build. | + +### Go + +| Buildpack | `heroku/go` | +|-----------|-------------| +| Render runtime | `go` | +| Default build command | `go build -o app .` | +| Detection | Check for `go.mod` | + +### Rust + +| Buildpack | `emk/rust` or custom | +|-----------|----------------------| +| Render runtime | `rust` | +| Default build command | `cargo build --release` | +| Start command | `./target/release/` | + +### Java / Scala / PHP / Multi-buildpack + +| Buildpack | Any not listed above | +|-----------|----------------------| +| Render runtime | `docker` | +| Action | Tell user they need a Dockerfile. Offer to generate one. | + +## Procfile Parsing + +Heroku Procfiles define process types. Extract these to map to Render services. + +### Format +``` +: +``` + +### Mapping Rules + +| Procfile entry | Render service type | Render MCP tool | `startCommand` | +|---------------|--------------------|-----------------|-----------------| +| `web: ` | Web Service | `create_web_service` | `` | +| `worker: ` | Background Worker | Blueprint `type: worker` | `` (MCP cannot create workers) | +| `clock: ` | Cron Job | `create_cron_job` | `` (ask user for schedule) | +| `release: ` | Pre-deploy command | N/A | Add as build command suffix | + +### Common Procfile Patterns + +**Node.js:** +``` +web: npm start +web: node server.js +web: next start -p $PORT +worker: node worker.js +``` + +**Python:** +``` +web: gunicorn app:app +web: gunicorn myproject.wsgi --log-file - +web: uvicorn main:app --host 0.0.0.0 --port $PORT +worker: celery -A myproject worker +clock: celery -A myproject beat +release: python manage.py migrate +``` + +**Ruby:** +``` +web: bundle exec puma -C config/puma.rb +worker: bundle exec sidekiq +release: bundle exec rake db:migrate +``` + +### Runtime Version Handling + +Do not assume or fabricate specific runtime versions for Render. Instead, carry over the version the Heroku app already uses: + +| Heroku file | What it contains | Render equivalent | +|---|---|---| +| `runtime.txt` | `python-3.11.6` | Set `PYTHON_VERSION=3.11.6` env var | +| `runtime.txt` | `ruby-3.2.2` | Render auto-detects from `ruby-3.2.2` in `Gemfile` | +| `.node-version` or `engines` in `package.json` | `18.17.0` | Set `NODE_VERSION=18.17.0` env var | +| `.go-version` or `go.mod` | `1.21` | Render auto-detects from `go.mod` | + +**Rules:** +- If the Heroku app pins a version via `runtime.txt` or similar, include the equivalent env var in the Blueprint +- If no version is pinned, do not specify one — Render uses its own defaults +- **Never state what Render's default version is** — it changes over time and any claim may be wrong + +### PORT Handling + +Heroku sets `$PORT` dynamically. Render also sets `PORT` (default 10000). Most Procfile commands work as-is. If the command hardcodes a port, it needs updating. + +- `--port $PORT` → works on both platforms +- `--port 5000` → change to `--port $PORT` or `--port 10000` +- Render auto-detects the port for common frameworks + +### Release Phase + +Heroku's `release:` process type runs before each deploy. Render has no direct equivalent. Options: +1. Append to build command: `pip install -r requirements.txt && python manage.py migrate` +2. Use Render's pre-deploy command feature (if available for the service type) +3. Flag for the user to handle manually + +## Static Site Detection + +If the Heroku app is a static site (React, Vue, Gatsby, etc.), use `create_static_site` instead of `create_web_service`. + +**Indicators:** +- Buildpack is `heroku-community/static` or `heroku/heroku-buildpack-static` +- Procfile is missing or only has `web: bin/boot` (static buildpack default) +- `package.json` has framework deps: `react-scripts`, `vue`, `gatsby`, `vite`, `@angular/cli` +- `static.json` file present in repo root + +**Static site config:** +| Framework | Build command | Publish path | +|-----------|--------------|--------------| +| Create React App | `npm install && npm run build` | `build` | +| Vite (React/Vue) | `npm install && npm run build` | `dist` | +| Gatsby | `npm install && gatsby build` | `public` | +| Next.js (static export) | `npm install && next build` | `out` | +| Angular | `npm install && ng build` | `dist/` | +| Hugo | `hugo` | `public` | diff --git a/plugins/render/skills/render-migrate-from-heroku/references/data-migration.md b/plugins/render/skills/render-migrate-from-heroku/references/data-migration.md new file mode 100644 index 0000000..d8d7c33 --- /dev/null +++ b/plugins/render/skills/render-migrate-from-heroku/references/data-migration.md @@ -0,0 +1,201 @@ +# Data Migration Guide + +Detailed steps for migrating Postgres and Redis data from Heroku to Render. The Render CLI's [`render psql`](https://render.com/docs/cli) handles the Postgres restore without requiring the Render database connection string. + +## 5a. Pre-migration checks + +Before generating any migration commands, verify readiness: + +1. **Render Postgres is provisioned** — call `list_postgres_instances()` to find the Postgres ID and confirm the database exists. Note the ID for `render psql` in Step 5c. +2. **Render Key Value is provisioned** (if Redis data needs migrating) — call `list_key_value()` to find the Key Value ID and confirm the instance exists. Note the ID for the Dashboard deeplink in Step 5d. +3. **Check source database size** — use Heroku MCP `pg_info` (look for `Data Size`), or ask the user to run `heroku pg:info -a ` and paste the output. +4. **Compare disk sizes** — warn if Heroku `Data Size` exceeds the `diskSizeGB` configured on the Render side. If it does, the user needs to increase `diskSizeGB` before restoring. +5. **Render CLI** — confirm the Render CLI is installed and authenticated (required for `render psql` restore): + + ```bash + render --version + render whoami + ``` + + If not installed, offer to install it: + - macOS: `brew install render` + - Linux/macOS: `curl -fsSL https://raw.githubusercontent.com/render-oss/cli/main/bin/install.sh | sh` + + If not authenticated, run `render login` to authorize via the Dashboard. + +6. **`pg_dump` and `pg_restore`** — check that the user has PostgreSQL client tools installed locally: + + ```bash + pg_dump --version + pg_restore --version + ``` + + Both are needed: `pg_dump` for all approaches, `pg_restore` for the traditional approach (databases over 2 GB). If not installed, suggest installing PostgreSQL client tools (e.g., `brew install libpq` on macOS, `apt install postgresql-client` on Linux). + +7. **Redis tools** (if migrating Redis data) — check for `redis-cli`: + + ```bash + redis-cli --version + ``` + +## 5b. Gather connection strings + +Collect the connection strings needed for the migration. The **Render Postgres** connection string is **not needed** — the Render CLI's `render psql` command connects directly using the Postgres ID from Step 5a. Only the Heroku source URLs and the Render Key Value URL (if applicable) are needed. + +**Heroku Postgres:** + +If Heroku MCP is available: + +``` +pg_credentials(app: "") +→ extract the connection URL from the response +→ store as HEROKU_DB_URL +``` + +If Heroku MCP is not available, ask the user to run `heroku pg:credentials:url DATABASE -a ` and paste the connection URL. + +**Render Key Value** (if migrating Redis data): + +The Render MCP does not return Key Value connection strings. Use the Key Value ID from Step 5a to construct a Dashboard deeplink and ask the user to copy the external connection URL: + +``` +Dashboard link: https://dashboard.render.com/d/ +→ ask user to open link, go to the Connections tab, copy the External Access URL +→ store as RENDER_REDIS_URL +``` + +**Heroku Redis** (if migrating Redis data): + +If Heroku MCP is available: + +``` +get_app_info(app: "") +→ read REDIS_URL from the config vars in the response +→ store as HEROKU_REDIS_URL +``` + +If Heroku MCP is not available, ask the user to run `heroku config:get REDIS_URL -a ` and paste the value. + +**Important:** Substitute all retrieved values into the commands in the following steps. Never present commands with placeholder URLs — always use the real connection strings. + +## 5c. Postgres migration + +Generate commands with the Heroku connection string from Step 5b and the Render Postgres ID from Step 5a substituted in. Present the full sequence to the user. + +**1. Put Heroku in maintenance mode** to stop writes during the migration: + +Use `maintenance_on` via Heroku MCP if available, or tell the user to run: + +```bash +heroku maintenance:on -a +``` + +**2. Dump and restore** — choose the approach based on database size (use `Data Size` from `pg_info` in Step 5a): + +**Render CLI approach** (databases under 2 GB — simplest, no Render connection string needed): + +Uses plain-text SQL dump and the [Render CLI's `render psql`](https://render.com/docs/cli) in non-interactive mode, which connects directly using the Postgres ID. + +```bash +# Dump from Heroku (plain-text SQL format) +pg_dump --clean --no-acl --no-owner -d > heroku_dump.sql +# Restore to Render via CLI +render psql --confirm -o text -- -f heroku_dump.sql +``` + +Replace `` with the Render Postgres ID from Step 5a (e.g., `dpg-abc123`). + +**Traditional approach** (databases 2-50 GB — faster, compressed, supports parallel restore): + +Uses custom-format dump with `pg_restore`. Requires the Render **external connection string** — ask the user to copy it from the Render Dashboard: + +``` +Dashboard link: https://dashboard.render.com/d/ +→ ask user to open link, go to the Connection tab, copy the External Connection String +→ store as RENDER_DB_URL +``` + +Then generate: + +```bash +# Dump from Heroku (compressed custom format) +pg_dump -Fc --no-acl --no-owner -d > heroku_dump.dump +# Restore to Render (parallel with 4 jobs) +pg_restore --clean --no-acl --no-owner -j 4 -d heroku_dump.dump +``` + +For pipe approach (avoids local disk usage): + +```bash +pg_dump -Fc --no-acl --no-owner -d | pg_restore --clean --no-acl --no-owner -d +``` + +Note: the pipe approach cannot use `-j` for parallel restore. + +**Very large databases** (over 50 GB): recommend the user [contact Render support](https://render.com/contact) for assisted migration. Do not generate commands — the process requires coordination. + +Replace `` with the actual Heroku connection string from Step 5b, and `` with the external connection string from the Dashboard. The user should see ready-to-run commands with real values. + +Remind the user to schedule a maintenance window. The app will be unavailable on Heroku from maintenance mode until DNS cutover to Render. + +## 5d. Key Value / Redis migration + +Most Heroku Redis instances are used as ephemeral caches and do not need data migration. Ask the user before proceeding: + +- **Ephemeral cache** (most common) — skip migration. The app will repopulate the cache after deployment on Render. No action needed. +- **Persistent data** — if the user confirms Redis holds persistent data (e.g., session store, queues, application state), generate migration commands using the connection strings from Step 5b: + + ```bash + # Dump from Heroku Redis + redis-cli -u --rdb heroku_dump.rdb + # Restore to Render Key Value (requires redis-cli 5.0+) + redis-cli -u --pipe < heroku_dump.rdb + ``` + + Replace `` with the Heroku Redis URL and `` with the Render Key Value External Access URL, both from Step 5b. Present ready-to-run commands with the real values. + + Note: RDB dump/restore may not be supported on all Heroku Redis plans. If it fails, the alternative is per-key `DUMP`/`RESTORE` or having the application re-seed the data. + +## 5e. Data validation + +After the user confirms the restore completed, validate data before moving to Step 6: + +**1. Check schema exists on Render:** + +``` +query_render_postgres( + postgresId: "", + sql: "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name" +) +``` + +**2. Compare row counts on key tables:** + +``` +query_render_postgres( + postgresId: "", + sql: "SELECT 'users' AS tbl, count(*) FROM users UNION ALL SELECT 'orders', count(*) FROM orders" +) +``` + +Adjust the table names to match the app. Pick 2-3 key tables that represent the core data. + +**3. Compare against Heroku source** (if Heroku MCP is available): + +``` +pg_psql(app: "", command: "SELECT 'users' AS tbl, count(*) FROM users UNION ALL SELECT 'orders', count(*) FROM orders") +``` + +**4. Present a side-by-side summary:** + +``` +DATA VALIDATION +───────────────────────────── +Table | Heroku | Render +users | 12,450 | 12,450 ✅ +orders | 84,321 | 84,321 ✅ +products | 1,203 | 1,203 ✅ +───────────────────────────── +``` + +If counts don't match, warn the user and suggest re-running the restore. Do not proceed to Step 6 until validation passes. diff --git a/plugins/render/skills/render-migrate-from-heroku/references/mcp-setup.md b/plugins/render/skills/render-migrate-from-heroku/references/mcp-setup.md new file mode 100644 index 0000000..1effdef --- /dev/null +++ b/plugins/render/skills/render-migrate-from-heroku/references/mcp-setup.md @@ -0,0 +1,102 @@ +# MCP Server Setup for Heroku to Render Migration + +The Render MCP server is recommended for direct service creation and automated verification, but not required for the Blueprint path. The Heroku MCP server is optional — it enables automatic discovery of config vars, add-on plans, and dyno sizes. + +## Render MCP Server (Recommended) + +Hosted at `https://mcp.render.com/mcp` (recommended, auto-updates). Requires a Render API key from [Account Settings](https://dashboard.render.com/u/*/settings#api-keys). + +Alternative: run locally via Docker or binary (see [Render MCP docs](https://render.com/docs/mcp-server)). +Source: [render-mcp-server](https://github.com/render-oss/render-mcp-server) + +**Detecting the user's tool:** Infer the AI tool from this skill's install path (`~/.cursor/skills/` = Cursor, `~/.claude/skills/` = Claude Code, `~/.codex/skills/` = Codex). If the path doesn't match a known tool, ask the user which tool they're using, then follow the matching section below. + +### Cursor + +1. Get a Render API key: +``` +https://dashboard.render.com/u/*/settings#api-keys +``` + +2. Add this to `~/.cursor/mcp.json` (replace ``): +```json +{ + "mcpServers": { + "render": { + "url": "https://mcp.render.com/mcp", + "headers": { + "Authorization": "Bearer " + } + } + } +} +``` + +3. Restart Cursor, then retry `list_services()`. + +### Claude Code + +1. Get a Render API key: +``` +https://dashboard.render.com/u/*/settings#api-keys +``` + +2. Add the MCP server with Claude Code (replace ``): +```bash +claude mcp add --transport http render https://mcp.render.com/mcp --header "Authorization: Bearer " +``` + +3. Restart Claude Code, then retry `list_services()`. + +### Codex + +1. Get a Render API key: +``` +https://dashboard.render.com/u/*/settings#api-keys +``` + +2. Set it in your shell: +```bash +export RENDER_API_KEY="" +``` + +3. Add the MCP server with the Codex CLI: +```bash +codex mcp add render --url https://mcp.render.com/mcp --bearer-token-env-var RENDER_API_KEY +``` + +4. Restart Codex, then retry `list_services()`. + +### Other Tools + +If using another AI tool, direct the user to the [Render MCP docs](https://render.com/docs/mcp-server) for that tool's setup steps and install method. + +## Heroku MCP Server (Optional) + +The Heroku MCP server enables automatic discovery of config vars, add-on plans, and dyno sizes. If it's not configured, the migration skill still works — it reads local project files and asks you to provide config var values manually. + +- Requires Heroku CLI v10.8.1+ installed globally +- `heroku mcp:start` uses existing CLI auth (no API key needed) +- Alternative: `npx -y @heroku/mcp-server` with `HEROKU_API_KEY` env var +- Source: [heroku-mcp-server](https://github.com/heroku/heroku-mcp-server) + +Add to your MCP config alongside the Render server: + +```json +{ + "mcpServers": { + "heroku": { + "command": "heroku", + "args": ["mcp:start"] + } + } +} +``` + +## Verification + +After configuring, test your connections: +- Ask: "List my Render services" — should return services via Render MCP (required) +- Ask: "List my Heroku apps" — should return apps via Heroku MCP (optional) + +If Render MCP fails, check your API key and restart your MCP client. If Heroku MCP is not configured, the migration skill still works — it reads local project files and asks you to provide config var values manually. diff --git a/plugins/render/skills/render-migrate-from-heroku/references/preflight-checklist.md b/plugins/render/skills/render-migrate-from-heroku/references/preflight-checklist.md new file mode 100644 index 0000000..aad7008 --- /dev/null +++ b/plugins/render/skills/render-migrate-from-heroku/references/preflight-checklist.md @@ -0,0 +1,69 @@ +# Pre-Flight Checklist + +Before creating any resources, validate the migration plan and present it to the user. Check each item below. + +## Validation Checks + +1. **Runtime supported?** If buildpack maps to `docker`, warn user they need a Dockerfile +2. **Worker dynos?** Flag these — can be defined in a Blueprint (`type: worker`, minimum plan `starter`), but cannot be created via MCP tools directly +3. **Release phase?** If Procfile has `release:`, suggest appending to build command +4. **Static site?** Check for static buildpack, `static.json`, or SPA framework deps — use `create_static_site` instead of `create_web_service`. See detection rules in the [buildpack mapping](buildpack-mapping.md). +5. **Third-party add-ons?** List any add-ons without direct Render equivalents (e.g., Papertrail, SendGrid) — user needs to find alternatives and update env vars +6. **Multiple process types?** If Procfile has >1 entry, each becomes a separate Render service (except `release:`) +7. **Repo URL available?** Verify a Git remote exists: + + ```bash + git remote -v + ``` + + If no remote exists, stop and guide the user to create a GitHub/GitLab/Bitbucket repo, add it as `origin`, and push before continuing. + + If the URL is SSH format, convert it to HTTPS for service creation and deeplinks: + + | SSH Format | HTTPS Format | + |---|---| + | `git@github.com:user/repo.git` | `https://github.com/user/repo` | + | `git@gitlab.com:user/repo.git` | `https://gitlab.com/user/repo` | + | `git@bitbucket.org:user/repo.git` | `https://bitbucket.org/user/repo` | + + **Conversion pattern:** Replace `git@:` with `https:///` and remove the `.git` suffix. + +8. **Database size?** If Postgres is Premium/large tier, recommend contacting Render support for assisted migration + +## Migration Plan Table + +Look up each Heroku dyno size and add-on plan in the [service mapping](service-mapping.md) to determine the correct Render plan. Then present the full plan as a table: + +``` +MIGRATION PLAN — [app-name] +───────────────────────────────── +CREATE (include only items that apply): + ✅ Web service ([runtime], [mapped-plan]) — startCommand: [cmd] + Heroku: [dyno-size] ($X/mo) → Render: [mapped-plan] ($Y/mo) + ✅ Background worker ([runtime], [mapped-plan]) — startCommand: [cmd] + Heroku: [dyno-size] ($X/mo) → Render: [mapped-plan] ($Y/mo) + ✅ Cron job ([mapped-plan]) — schedule: [cron expr] — command: [cmd] + ✅ Postgres ([mapped-plan], diskSizeGB: [size]) + Heroku: [plan-slug] ($X/mo) → Render: [mapped-plan] ($Y/mo + storage) + ✅ Key Value ([mapped-plan]) + Heroku: [plan-slug] ($X/mo) → Render: [mapped-plan] ($Y/mo) + +ESTIMATED MONTHLY COST: + Heroku: $[total]/mo → Render: $[total]/mo + (Render storage billed separately at $0.30/GB/mo in 5 GB increments; cannot be scaled down once provisioned) + +METHOD: [Blueprint | MCP Direct Creation] + +MANUAL STEPS REQUIRED: + ⚠️ Custom domain: [domain] — configure after deploy + ⚠️ Replace add-on: [name] → find alternative + +ENV VARS: [N] to migrate, [M] filtered out +DATABASE: [size] — pg_dump/render psql required +───────────────────────────────── +Proceed? (y/n) +``` + +Use the pricing columns in the [service mapping](service-mapping.md) to calculate costs. Sum up the Render $/mo for each service, database, and Key Value store. For Postgres, note that storage is billed separately. + +Wait for user confirmation before creating any resources. diff --git a/plugins/render/skills/render-migrate-from-heroku/references/service-mapping.md b/plugins/render/skills/render-migrate-from-heroku/references/service-mapping.md new file mode 100644 index 0000000..e2b3618 --- /dev/null +++ b/plugins/render/skills/render-migrate-from-heroku/references/service-mapping.md @@ -0,0 +1,169 @@ +# Heroku → Render Service Mapping + +## How to Use This Reference + +Look up the Heroku plan from `ps_list` (dyno size) or `list_addons` (add-on plan slug) and use the corresponding Render plan in the Blueprint or MCP creation call. If the Heroku plan is unknown, use the fallback default. + +## Compute (Dynos → Services) + +Match by RAM to avoid out-of-memory issues. Worker and cron dynos use the same size mapping as web dynos. + +| Heroku dyno | Heroku RAM | Heroku $/mo | Render `plan` value | Render RAM | Render $/mo | +|---|---|---|---|---|---| +| Eco | 512 MB | $5 | `starter` | 512 MB | $7 | +| Basic | 512 MB | $7 | `starter` | 512 MB | $7 | +| Standard-1X | 512 MB | $25 | `starter` | 512 MB | $7 | +| Standard-2X | 1 GB | $50 | `standard` | 2 GB | $25 | +| Performance-M | 2.5 GB | $250 | `pro` | 4 GB | $85 | +| Performance-L | 14 GB | $500 | `pro max` | 16 GB | $225 | +| Performance-L-RAM | 30 GB | $500 | `pro ultra` | 32 GB | $450 | +| Performance-XL | 62 GB | $750 | Custom | 64 GB | ~$712 | +| Performance-2XL | 126 GB | $1,500 | Custom | 128 GB | ~$1,426 | + +**Fallback default:** `starter` (when Heroku dyno size is unknown) + +**Notes:** +- Worker dynos on Heroku can be any size (Standard-1X, Performance-M, etc.) — use the same mapping based on the dyno size reported by `ps_list` +- Cron jobs use the same mapping — Render cron plans match web/worker plans +- Performance-L-RAM has 4 CPU (half of Pro Ultra's 8 CPU). For non-enterprise customers, default to `pro ultra`. Enterprise customers may negotiate custom pricing (~$356) with Render for a closer CPU match. +- For Performance-XL and Performance-2XL, instruct the user to [contact Render](https://render.com/contact) for custom sizing. The estimates above (~$712, ~$1,426) are based on proportional custom pricing and may vary. +- Enterprise contracts change per-dyno pricing — the "units per dyno" multiplier varies (e.g., Performance-M = 8 units, Performance-L = 16 units of a Standard-1X). Heroku prices above reflect pay-as-you-go rates. + +## Postgres (Heroku Postgres → Render Postgres) + +Heroku has deprecated Mini and Basic plans. Current tiers are Essential, Standard, and Premium. + +Render has three Postgres tiers: +- **Basic** — entry-level, for development and low-traffic apps +- **Pro** — balanced CPU-to-RAM ratio (1:4), for production workloads +- **Accelerated** — memory-optimized CPU-to-RAM ratio (1:8), for high-performance and memory-intensive workloads + +Map Heroku Essential → Render Basic. For Standard and Premium, both map to the **same Render plan per tier** — Standard as a single instance, Premium with HA enabled (doubling the Render cost). Tiers 0 and 2 map to Render Pro; tiers 3 through 9 map to Render Accelerated. + +### Essential and legacy plans → Render Basic + +| Heroku plan | Heroku disk | Heroku $/mo | Render `plan` value | Render `diskSizeGB` | Render $/mo | +|---|---|---|---|---|---| +| Essential-0 | 1 GB | $5 | `basic-256mb` | 1 | $6 + storage | +| Essential-1 | 10 GB | $9 | `basic-256mb` | 10 | $6 + storage | +| Essential-2 | 32 GB | $20 | `basic-1gb` | 32 | $19 + storage | +| Mini (legacy, EOL) | 1 GB | $5 | `basic-256mb` | 1 | $6 + storage | +| Basic (legacy, EOL) | 10 GB | $9 | `basic-256mb` | 10 | $6 + storage | + +### Standard and Premium plans → Render Pro / Accelerated + +Heroku Standard and Premium share the same tier numbering (X-0 through X-9) with identical vCPU, RAM, and storage specs. The difference is that **Premium includes HA by default**. On Render, both map to the same plan — enable HA on Render to match Premium (which doubles the Render base price for a separate replica). + +| Heroku tier | Heroku Standard $/mo | Heroku Premium $/mo | vCPU | RAM | Storage | Render `plan` value | Render `diskSizeGB` | Render $/mo | Render HA $/mo | +|---|---|---|---|---|---|---|---|---|---| +| X-0 | $50 | $200 | 2 | 4 GB | 64 GB | `pro-4gb` | 65 | $55 + storage | $110 + storage | +| X-2 | $200 | $350 | 2 | 8 GB | 256 GB | `pro-8gb` | 256 | $100 + storage | $200 + storage | +| X-3 | $400 | $750 | 2 | 15 GB | 512 GB | `accelerated-16gb` | 512 | $160 + storage | $320 + storage | +| X-4 | $750 | $1,200 | 4 | 30 GB | 768 GB | `accelerated-32gb` | 770 | $350 + storage | $700 + storage | +| X-5 | $1,400 | $2,500 | 8 | 61 GB | 1 TB | `accelerated-64gb` | 1024 | $750 + storage | $1,500 + storage | +| X-6 | $2,000 | $3,500 | 16 | 122 GB | 1.5 TB | `accelerated-128gb` | 1536 | $1,500 + storage | $3,000 + storage | +| X-7 | $3,500 | $6,000 | 32 | 244 GB | 2 TB | `accelerated-256gb` | 2048 | $2,500 + storage | $5,000 + storage | +| X-8 | $4,500 | $8,500 | 64 | 488 GB | 3 TB | `accelerated-512gb` | 3072 | $6,000 + storage | $12,000 + storage | +| X-9 | $5,800 | $11,000 | 96 | 768 GB | 4 TB | `accelerated-768gb` | 4096 | $9,000 + storage | $18,000 + storage | + +**How to read the tier number:** The add-on plan slug from `list_addons` looks like `heroku-postgresql:standard-0` or `heroku-postgresql:premium-4` — the number after the hyphen is the tier (X-0, X-4, etc.). Both Standard-4 and Premium-4 map to the same Render plan (`accelerated-32gb`); Premium just needs HA enabled. + +**HA pricing note:** Heroku Premium pricing includes HA by default. To match this on Render, enable HA in the Dashboard or Blueprint, which adds a standby replica at the same cost as the primary (effectively 2x). This significantly impacts pricing competitiveness on larger instances — present both the single-instance and HA costs to the user so they can make an informed decision. + +### Disk sizing + +On Render, storage is billed separately at **$0.30/GB/month** and configured via the `diskSizeGB` field in the Blueprint. Storage is provisioned in **5 GB increments** (minimum 1 GB) and **cannot be scaled down** once provisioned. + +**Heuristic:** Carry over the Heroku disk size as the `diskSizeGB` value. Since `diskSizeGB` must be 1 or a multiple of 5, round up to the nearest valid value. + +**Disclaimer to present to the user:** Heroku bundles a fixed storage allocation with each plan (e.g., Standard-4 includes 768 GB). On Render, compute and storage are billed separately — compute is the plan price above, and storage is $0.30/GB/month in 5 GB increments. Storage cannot be scaled down once provisioned, so right-size based on actual usage rather than the Heroku allocation. Check your current disk usage with `heroku pg:info` (look for `Data Size`) — if your actual data is much smaller than the allocated disk, start with a smaller `diskSizeGB` to save on storage costs. You can always expand later from the Render Dashboard. + +**Fallback default:** `basic-1gb` with no `diskSizeGB` (when Heroku Postgres plan is unknown — Render uses a default disk size) + +**Notes:** +- Render Pro and Accelerated both support HA (enable separately in Dashboard or Blueprint) +- For databases beyond tier X-9, contact Render support +- Get actual disk usage from `pg_info` (`Data Size` field) to inform the `diskSizeGB` recommendation + +## Key Value (Heroku Redis / Key-Value Store → Render Key Value) + +Heroku has rebranded Redis as "Key-Value Store" (Valkey-based). Heroku Redis plans use non-sequential numbering (0, 1, 2, 3, 5, 7, 9, 10, 12, 14). Match by the exact plan number from `list_addons`. + +| Heroku plan | Heroku memory | Heroku connections | Heroku $/mo | Render `plan` value | Render RAM | Render connections | Render $/mo | +|---|---|---|---|---|---|---|---| +| Mini | 25 MB | 20 | $3 | `free` | 25 MB | 50 | $0 | +| Premium-0 | 50 MB | 40 | $15 | `starter` | 256 MB | 250 | $10 | +| Premium-1 | 100 MB | 80 | $30 | `starter` | 256 MB | 250 | $10 | +| Premium-2 | 250 MB | 200 | $60 | `starter` | 256 MB | 250 | $10 | +| Premium-3 | 500 MB | 400 | $120 | `standard` | 1 GB | 1,000 | $32 | +| Premium-5 | 1 GB | 1,000 | $200 | `standard` | 1 GB | 1,000 | $32 | +| Premium-7 | 7 GB | 10,000 | $750 | `pro plus` | 10 GB | 10,000 | $250 | +| Premium-9 | 10 GB | 25,000 | $1,450 | `pro max` | 20 GB | 20,000 | $550 | +| Premium-10 | 25 GB | 40,000 | $3,500 | Custom | — | — | Contact Render | +| Premium-12 | 50 GB | 65,000 | $6,500 | Custom | — | — | Contact Render | +| Premium-14 | 100 GB | 65,000 | $12,500 | Custom | — | — | Contact Render | + +**Fallback default:** `starter` (when Heroku Redis plan is unknown) + +**Notes:** +- Redis pricing heavily favors Render — most tiers cost significantly less on Render than the Heroku equivalent. +- The add-on plan slug from `list_addons` looks like `heroku-redis:mini` or `heroku-redis:premium-0` — use the part after the colon to look up the mapping. Plans 4, 6, 8, 11, and 13 do not exist on Heroku. +- Render Key Value requires `ipAllowList` in the Blueprint (use `0.0.0.0/0` for public access) +- Neither Heroku nor Render supports Redis HA. +- For Premium-10 and above, instruct the user to [contact Render](https://render.com/contact) for custom sizing. + +## Runtime Mapping + +| Heroku Buildpack | Render Runtime | `runtime` param | +|---|---|---| +| heroku/nodejs | Node | `node` | +| heroku/python | Python | `python` | +| heroku/go | Go | `go` | +| heroku/ruby | Ruby | `ruby` | +| heroku/java | Docker | `docker` | +| heroku/php | Docker | `docker` | +| heroku/scala | Docker | `docker` | +| Multi-buildpack | Docker | `docker` | + +## Region Mapping + +| Heroku Region | Render Region | `region` param | +|---|---|---| +| us | Oregon (default) | `oregon` | +| eu | Frankfurt | `frankfurt` | + +## Not Directly Mappable (Manual) + +These Heroku features require manual alternatives on Render: +- **Heroku Pipelines** → Use Render Preview Environments + manual promotion +- **Review Apps** → Render Pull Request Previews +- **Heroku Add-ons Marketplace** → Find equivalent third-party services +- **Heroku ACM (SSL)** → Render auto-provisions TLS for custom domains +- **Private Spaces** → Contact Render for private networking options +- **Heroku Kafka** → Not supported on Render. Recommend cloud providers (Confluent Cloud, AWS MSK, etc.) +- **Hirefire** → Not supported on Render. Recommend Render's native [horizontal autoscaling](https://render.com/docs/scaling) or JudoScale (which is supported). +- **Bandwidth** → Heroku allows ~2 TB/app/month (rarely enforced). Render bandwidth varies by workspace plan: 100 GB (Hobby), 500 GB (Professional), 1 TB (Organization). Additional bandwidth is billed at $0.10/GB. + +## Environment Variables to Filter + +Always exclude these when migrating env vars: + +**Render auto-generates:** +- `DATABASE_URL` +- `REDIS_URL`, `REDIS_TLS_URL` + +**Heroku-specific (no Render equivalent):** +- `HEROKU_APP_NAME` +- `HEROKU_SLUG_COMMIT` +- `HEROKU_SLUG_DESCRIPTION` +- `HEROKU_DYNO_ID` +- `HEROKU_RELEASE_VERSION` +- `PORT` (Render sets its own) + +**Add-on connection strings (replace with new service URLs):** +- `PAPERTRAIL_*` +- `SENDGRID_*` +- `CLOUDAMQP_*` +- `BONSAI_*` +- `FIXIE_*` +- Any other `*_URL` vars pointing to Heroku add-on services