Skip to content

Commit a3b36b5

Browse files
R4ph-tgithub-actions[bot]
authored andcommitted
Sync skills from render-oss/skills
1 parent c633e60 commit a3b36b5

7 files changed

Lines changed: 1198 additions & 0 deletions

File tree

plugins/render/skills/render-migrate-from-heroku/SKILL.md

Lines changed: 312 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# Blueprint Example: Heroku Migration with Project/Environment Pattern
2+
3+
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.
4+
5+
References: [Blueprint docs](https://render.com/docs/blueprint-spec#projects-and-environments) | [Blueprint YAML JSON schema](https://render.com/schema/render.yaml.json)
6+
7+
## Full Example
8+
9+
Assumes a Node.js app named `acme-app` migrating from Heroku US region.
10+
11+
```yaml
12+
previews:
13+
generation: off
14+
projects:
15+
- name: acme-app
16+
environments:
17+
- name: production
18+
services:
19+
# Web service (from Heroku web dyno)
20+
- type: web
21+
name: acme-app-web
22+
runtime: node
23+
plan: starter
24+
region: oregon
25+
buildCommand: npm ci && npm run build
26+
startCommand: npm start
27+
healthCheckPath: /health
28+
envVars:
29+
- key: NODE_ENV
30+
value: production
31+
- key: DATABASE_URL
32+
fromDatabase:
33+
name: acme-app-db
34+
property: connectionString
35+
- key: REDIS_URL
36+
fromService:
37+
type: keyvalue
38+
name: acme-app-cache
39+
property: connectionString
40+
- key: APP_NAME
41+
value: acme-app
42+
- key: LOG_LEVEL
43+
value: info
44+
- key: STRIPE_API_KEY
45+
sync: false
46+
- key: JWT_SECRET
47+
sync: false
48+
49+
# Background worker (from Heroku worker dyno)
50+
- type: worker
51+
name: acme-app-worker
52+
runtime: node
53+
plan: starter
54+
region: oregon
55+
buildCommand: npm ci
56+
startCommand: node worker.js
57+
envVars:
58+
- key: DATABASE_URL
59+
fromDatabase:
60+
name: acme-app-db
61+
property: connectionString
62+
- key: REDIS_URL
63+
fromService:
64+
type: keyvalue
65+
name: acme-app-cache
66+
property: connectionString
67+
- key: APP_NAME
68+
value: acme-app
69+
- key: LOG_LEVEL
70+
value: info
71+
- key: STRIPE_API_KEY
72+
sync: false
73+
74+
# Cron job (from Heroku Scheduler or clock dyno)
75+
- type: cron
76+
name: acme-app-cron
77+
runtime: node
78+
plan: starter
79+
region: oregon
80+
schedule: "0 * * * *"
81+
buildCommand: npm ci
82+
startCommand: node scripts/scheduled-task.js
83+
envVars:
84+
- key: DATABASE_URL
85+
fromDatabase:
86+
name: acme-app-db
87+
property: connectionString
88+
- key: APP_NAME
89+
value: acme-app
90+
- key: STRIPE_API_KEY
91+
sync: false
92+
93+
# Key Value (from Heroku Data for Redis)
94+
- type: keyvalue
95+
name: acme-app-cache
96+
plan: starter
97+
ipAllowList:
98+
- source: 0.0.0.0/0
99+
description: everywhere
100+
101+
databases:
102+
# Postgres (from Heroku Postgres)
103+
- name: acme-app-db
104+
plan: basic-1gb
105+
diskSizeGB: 10 # carried over from Heroku plan allocation
106+
107+
```
108+
109+
## Key Patterns
110+
111+
### Service references
112+
113+
Use `fromDatabase` and `fromService` instead of hardcoding connection strings:
114+
115+
```yaml
116+
# Postgres connection string
117+
- key: DATABASE_URL
118+
fromDatabase:
119+
name: acme-app-db
120+
property: connectionString
121+
122+
# Key Value (Redis) connection string
123+
- key: REDIS_URL
124+
fromService:
125+
type: keyvalue
126+
name: acme-app-cache
127+
property: connectionString
128+
```
129+
130+
### Environment variables
131+
132+
Define env vars directly on each service. Do not use `envVarGroups` — they can cause misapplication issues during Blueprint sync.
133+
134+
### Secrets
135+
136+
Mark secrets with `sync: false` so the user is prompted in the Dashboard:
137+
138+
```yaml
139+
- key: STRIPE_API_KEY
140+
sync: false
141+
```
142+
143+
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`.
144+
145+
## Blueprint Rules
146+
147+
Follow these rules when generating a `render.yaml` for migration:
148+
149+
- **Always use the `projects:`/`environments:` pattern** — the YAML must start with a `projects:` key. Never use flat top-level `services:` or `databases:` keys.
150+
- **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.
151+
- **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.
152+
- Always include `previews: { generation: off }` at the root level to disable preview environments by default.
153+
- Use `fromDatabase` for `DATABASE_URL` — never hardcode connection strings.
154+
- Use `fromService` with `type: keyvalue` and `property: connectionString` for `REDIS_URL`.
155+
- Define env vars directly on each service (do not use `envVarGroups`).
156+
- Mark secrets with `sync: false` (user fills these in the Dashboard during Blueprint apply).
157+
- Map region from Heroku using the [service mapping](service-mapping.md).
158+
- Only include service/database blocks that the Heroku app actually uses.
159+
160+
## Plan Selection
161+
162+
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.
163+
164+
**Fallback defaults** (when the Heroku plan is unknown):
165+
166+
| Service type | Fallback plan |
167+
|---|---|
168+
| Web / worker / cron / static / pserv | `starter` |
169+
| Key Value | `starter` |
170+
| Postgres | `basic-1gb` |
171+
172+
## Adapting This Example
173+
174+
- **Python app:** Change `runtime: node` to `runtime: python`, update build/start commands (e.g., `pip install -r requirements.txt`, `gunicorn app:app`)
175+
- **Ruby app:** Change to `runtime: ruby`, update commands (e.g., `bundle install`, `bundle exec puma`)
176+
- **No worker:** Remove the `type: worker` service block
177+
- **No cron:** Remove the `type: cron` service block
178+
- **No Redis:** Remove the `type: keyvalue` service block and any `REDIS_URL` env var references
179+
- **No Postgres:** Remove the `databases` section and any `DATABASE_URL` env var references
180+
- **Static site:** Replace `type: web` with `runtime: static` and add `staticPublishPath`
181+
- **EU region:** Change `region: oregon` to `region: frankfurt` (maps from Heroku `eu` region)
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Buildpack, Procfile, and Build Command Mapping
2+
3+
## Buildpack → Render Runtime + Build Command
4+
5+
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.
6+
7+
### Node.js
8+
9+
| Buildpack | `heroku/nodejs` or `heroku-community/nodejs` |
10+
|-----------|----------------------------------------------|
11+
| Render runtime | `node` |
12+
| Default build command | `npm install && npm run build` |
13+
| Fallback build (no build script) | `npm install` |
14+
| Detection | Check `package.json` for `build` script. If missing, use fallback. |
15+
16+
**Common variations:**
17+
- Yarn: `yarn install && yarn build` (detect via `yarn.lock` presence)
18+
- pnpm: `pnpm install && pnpm run build` (detect via `pnpm-lock.yaml`)
19+
- TypeScript without build script: `npm install && npx tsc`
20+
- Next.js: `npm install && npm run build` (start: `npm start` or `next start`)
21+
- Vite/CRA (static): use `create_static_site` instead, publishPath `dist` or `build`
22+
23+
### Python
24+
25+
| Buildpack | `heroku/python` |
26+
|-----------|-----------------|
27+
| Render runtime | `python` |
28+
| Default build command | `pip install -r requirements.txt` |
29+
| Django build | `pip install -r requirements.txt && python manage.py collectstatic --noinput` |
30+
| Detection | Check for `requirements.txt`, `Pipfile`, or `pyproject.toml` |
31+
32+
**Common variations:**
33+
- Pipenv: `pip install pipenv && pipenv install`
34+
- Poetry: `pip install poetry && poetry install`
35+
- Django: look for `collectstatic` in Procfile or `django` in requirements
36+
- FastAPI/Flask: straightforward `pip install -r requirements.txt`
37+
38+
### Ruby
39+
40+
| Buildpack | `heroku/ruby` |
41+
|-----------|---------------|
42+
| Render runtime | `ruby` |
43+
| Default build command | `bundle install` |
44+
| Rails build | `bundle install && bundle exec rake assets:precompile` |
45+
| Detection | Check for `Gemfile`. If `rails` gem present, use Rails build. |
46+
47+
### Go
48+
49+
| Buildpack | `heroku/go` |
50+
|-----------|-------------|
51+
| Render runtime | `go` |
52+
| Default build command | `go build -o app .` |
53+
| Detection | Check for `go.mod` |
54+
55+
### Rust
56+
57+
| Buildpack | `emk/rust` or custom |
58+
|-----------|----------------------|
59+
| Render runtime | `rust` |
60+
| Default build command | `cargo build --release` |
61+
| Start command | `./target/release/<binary-name>` |
62+
63+
### Java / Scala / PHP / Multi-buildpack
64+
65+
| Buildpack | Any not listed above |
66+
|-----------|----------------------|
67+
| Render runtime | `docker` |
68+
| Action | Tell user they need a Dockerfile. Offer to generate one. |
69+
70+
## Procfile Parsing
71+
72+
Heroku Procfiles define process types. Extract these to map to Render services.
73+
74+
### Format
75+
```
76+
<process-type>: <command>
77+
```
78+
79+
### Mapping Rules
80+
81+
| Procfile entry | Render service type | Render MCP tool | `startCommand` |
82+
|---------------|--------------------|-----------------|-----------------|
83+
| `web: <cmd>` | Web Service | `create_web_service` | `<cmd>` |
84+
| `worker: <cmd>` | Background Worker | Blueprint `type: worker` | `<cmd>` (MCP cannot create workers) |
85+
| `clock: <cmd>` | Cron Job | `create_cron_job` | `<cmd>` (ask user for schedule) |
86+
| `release: <cmd>` | Pre-deploy command | N/A | Add as build command suffix |
87+
88+
### Common Procfile Patterns
89+
90+
**Node.js:**
91+
```
92+
web: npm start
93+
web: node server.js
94+
web: next start -p $PORT
95+
worker: node worker.js
96+
```
97+
98+
**Python:**
99+
```
100+
web: gunicorn app:app
101+
web: gunicorn myproject.wsgi --log-file -
102+
web: uvicorn main:app --host 0.0.0.0 --port $PORT
103+
worker: celery -A myproject worker
104+
clock: celery -A myproject beat
105+
release: python manage.py migrate
106+
```
107+
108+
**Ruby:**
109+
```
110+
web: bundle exec puma -C config/puma.rb
111+
worker: bundle exec sidekiq
112+
release: bundle exec rake db:migrate
113+
```
114+
115+
### Runtime Version Handling
116+
117+
Do not assume or fabricate specific runtime versions for Render. Instead, carry over the version the Heroku app already uses:
118+
119+
| Heroku file | What it contains | Render equivalent |
120+
|---|---|---|
121+
| `runtime.txt` | `python-3.11.6` | Set `PYTHON_VERSION=3.11.6` env var |
122+
| `runtime.txt` | `ruby-3.2.2` | Render auto-detects from `ruby-3.2.2` in `Gemfile` |
123+
| `.node-version` or `engines` in `package.json` | `18.17.0` | Set `NODE_VERSION=18.17.0` env var |
124+
| `.go-version` or `go.mod` | `1.21` | Render auto-detects from `go.mod` |
125+
126+
**Rules:**
127+
- If the Heroku app pins a version via `runtime.txt` or similar, include the equivalent env var in the Blueprint
128+
- If no version is pinned, do not specify one — Render uses its own defaults
129+
- **Never state what Render's default version is** — it changes over time and any claim may be wrong
130+
131+
### PORT Handling
132+
133+
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.
134+
135+
- `--port $PORT` → works on both platforms
136+
- `--port 5000` → change to `--port $PORT` or `--port 10000`
137+
- Render auto-detects the port for common frameworks
138+
139+
### Release Phase
140+
141+
Heroku's `release:` process type runs before each deploy. Render has no direct equivalent. Options:
142+
1. Append to build command: `pip install -r requirements.txt && python manage.py migrate`
143+
2. Use Render's pre-deploy command feature (if available for the service type)
144+
3. Flag for the user to handle manually
145+
146+
## Static Site Detection
147+
148+
If the Heroku app is a static site (React, Vue, Gatsby, etc.), use `create_static_site` instead of `create_web_service`.
149+
150+
**Indicators:**
151+
- Buildpack is `heroku-community/static` or `heroku/heroku-buildpack-static`
152+
- Procfile is missing or only has `web: bin/boot` (static buildpack default)
153+
- `package.json` has framework deps: `react-scripts`, `vue`, `gatsby`, `vite`, `@angular/cli`
154+
- `static.json` file present in repo root
155+
156+
**Static site config:**
157+
| Framework | Build command | Publish path |
158+
|-----------|--------------|--------------|
159+
| Create React App | `npm install && npm run build` | `build` |
160+
| Vite (React/Vue) | `npm install && npm run build` | `dist` |
161+
| Gatsby | `npm install && gatsby build` | `public` |
162+
| Next.js (static export) | `npm install && next build` | `out` |
163+
| Angular | `npm install && ng build` | `dist/<project-name>` |
164+
| Hugo | `hugo` | `public` |

0 commit comments

Comments
 (0)