You’ve seen each pattern on its own. Now the payoff: a single render.yaml that uses all of them together - projects + environments, env groups, every wiring primitive, previews, a monorepo buildFilter, a disk on a worker, a Docker web service, and an image-runtime cron.
It’s longer than anything you’d write greenfield. Treat it as a reference: scan it, copy the bits that match your shape, and lean on the validation loop from step 01 to keep your version honest.
The composed Blueprint
previews: generation: automatic expireAfterDays: 3
projects: - name: acme-platform
envVarGroups: - name: shared-config envVars: - key: LOG_LEVEL value: info - key: NODE_ENV value: production
environments: - name: production
envVarGroups: - name: production-secrets envVars: - key: STRIPE_API_KEY - key: SLACK_WEBHOOK
databases: - name: app-db plan: basic-1gb previewPlan: basic-256mb previewDiskSizeGB: 5
services: - type: web name: api runtime: docker plan: standard previewPlan: starter rootDir: apps/api dockerfilePath: ./apps/api/Dockerfile dockerContext: . dockerCommand: node dist/server.js healthCheckPath: /health buildFilter: paths: - apps/api/** - packages/shared-types/** - pnpm-lock.yaml ignoredPaths: - apps/api/**/*.test.ts scaling: minInstances: 2 maxInstances: 10 targetCPUPercent: 70 envVars: - key: DATABASE_URL fromDatabase: name: app-db property: connectionString - key: REDIS_URL fromService: name: cache type: keyvalue property: connectionString - key: AUTH_URL fromService: name: auth type: pserv property: hostport - key: JWT_SECRET generateValue: true - fromGroup: shared-config - fromGroup: production-secrets
- type: pserv name: auth runtime: node plan: starter rootDir: apps/auth buildCommand: pnpm install && pnpm --filter auth build startCommand: pnpm --filter auth start buildFilter: paths: - apps/auth/** - packages/shared-types/** - pnpm-lock.yaml envVars: - key: DATABASE_URL fromDatabase: name: app-db property: connectionString - key: API_SECRET generateValue: true - fromGroup: shared-config
- type: keyvalue name: cache plan: starter maxmemoryPolicy: allkeys-lru ipAllowList: []
- type: worker name: media-processor runtime: node plan: starter rootDir: apps/media buildCommand: pnpm install && pnpm --filter media build startCommand: pnpm --filter media start disk: name: media-cache mountPath: /var/data/media sizeGB: 20 previews: generation: off envVars: - key: DATABASE_URL fromDatabase: name: app-db property: connectionString - fromGroup: shared-config
- type: cron name: nightly-cleanup runtime: image schedule: "0 3 * * *" image: url: ghcr.io/acme/cleanup:v2.1.0 creds: fromRegistryCreds: name: ghcr envVars: - key: DATABASE_URL fromDatabase: name: app-db property: connectionString - fromGroup: shared-config
- name: staging envVarGroups: - name: staging-secrets envVars: - key: STRIPE_API_KEY - key: SLACK_WEBHOOK
databases: - name: app-db plan: basic-256mb
services: - type: web name: api runtime: docker plan: starter rootDir: apps/api dockerfilePath: ./apps/api/Dockerfile dockerContext: . dockerCommand: node dist/server.js healthCheckPath: /health envVars: - key: DATABASE_URL fromDatabase: name: app-db property: connectionString - fromGroup: shared-config - fromGroup: staging-secretsThat’s the whole stack: a Docker-built web service, a Node private service for auth, a Key Value cache, a disk-backed worker for media, and a cron that runs a prebuilt image - all in production, with a slimmer staging mirror, and previews on for everything except the worker.
What each pattern brings
| Step | Pattern | Where it shows up |
|---|---|---|
| 02 | Projects + environments | projects → environments → production / staging |
| 03 | Env groups, scoped | shared-config (project) + production-secrets (env) |
| 04 | All six wiring primitives | fromDatabase, fromService (kv + pserv), fromGroup, generateValue, plain value |
| 05 | Previews + per-service overrides | previews.generation: automatic, worker opts out |
| 06 | rootDir + buildFilter | Per-service paths including packages/** + lockfile |
| 07 | Disk + autoscaling | Disk on worker, autoscaling on web |
| 08 | runtime: docker and runtime: image | Web (docker) + cron (image) |
Most real Blueprints land somewhere on this spectrum - usually a third of these features. Composability is the point: each pattern is independent, and they don’t fight each other.
The CI workflow that keeps it honest
The Blueprint above is exactly the kind of file you don’t want to merge unvalidated. Drop this into .github/workflows/render-blueprint.yml and every PR that touches it runs the same validation you ran locally:
name: Validate render.yaml
on: pull_request: paths: - "render.yaml" - ".github/workflows/render-blueprint.yml"
jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Install the Render CLI run: | curl -fsSL https://raw.githubusercontent.com/render-oss/cli/main/bin/install.sh | sh echo "$HOME/.render/bin" >> "$GITHUB_PATH"
- name: Validate Blueprint run: render blueprints validateThe paths: filter keeps the job idle on PRs that don’t touch the Blueprint, so it costs almost nothing on a busy repo. GitLab and Bitbucket pipelines follow the same shape - install the CLI, run render blueprints validate, fail the job on a non-zero exit.
Where to go next
The patterns in this cookbook give you the shape of a production-grade Blueprint. The next milestones are operational:
- Deploy a Blueprint end-to-end - the Deploy SF Pulse on Render tutorial walks the deploy flow with a real example app.
- Author Blueprints with AI assistance - your AI coding tool already has the
render-blueprintsskill installed. Hand it the schema URL and it’ll generate, validate, and explain Blueprints inline. - Connect Render’s MCP server - point Claude, Cursor, or Codex at
mcp.render.comand your AI tool can list services, fetch logs, and trigger deploys without leaving the editor.
What’s deliberately out of scope
This cookbook covered eight of the patterns most teams reach for. A few things you’ll bump into eventually but that aren’t here:
- Immutable-fields-and-recreation patterns - the rules for which database/service fields can never be edited (e.g.
databaseName,region,postgresMajorVersion) and the recipes for safely working around them. - End-to-end deploy walkthroughs - handled by the existing render-workflows, Deploy SF Pulse on Render, and Extend SF Pulse with Render Workflows tutorials.
- Workflow-specific Blueprint quirks -
runtime: workflowisn’t supported inrender.yamlyet; create the Workflow service from the Render Dashboard.
What you learned
- A real Blueprint composes projects/environments, env groups, wiring primitives, previews, monorepo filters, disks, scaling, and Docker/image runtimes - all without fighting each other
- Pair the Blueprint with a CI workflow that runs `render blueprints validate` on every PR that touches the file
- When you're ready to deploy, hand off to the workflow-specific tutorials, the `render-deploy` skill, or the Render MCP server