Render Tutorials
Advanced render.yaml Blueprint patterns

Cross-service wiring deep dive

⏱ 10 min

The single biggest reason teams adopt Blueprints isn’t the YAML - it’s that services can pull connection details from each other instead of you copy-pasting postgres://... into the Render Dashboard. This step is the full reference for those wiring primitives, organized so you can recognize the right one at a glance.

The six env var patterns

Every entry under envVars: is one of these:

PatternWhat it producesWhen to use
valueInline literalNon-secret config you’re happy committing
generateValueRender-generated random secretApp-internal secrets (JWT, CRON token)
sync: falseRender prompts at sync timeThird-party API keys (Stripe, OpenAI)
fromDatabaseDB connection string or partPostgres credentials
fromServiceAnother service’s host, port, or env varKey Value, private services, web services
fromGroupBundle of vars from an envVarGroupShared config or secrets

A single service can mix all six. The mental model: each entry has one source. Don’t combine value: and fromDatabase: on the same key.

flowchart LR
  subgraph sources [Sources]
    db[(Postgres)]
    kv[(Key Value)]
    psv[Private service]
    grp[Env group]
    rnd[Render generator]
    user[You<br/>(Render Dashboard prompt)]
  end

  subgraph api [api web service]
    direction TB
    apiEnvs["envVars:<br/>DATABASE_URL<br/>REDIS_URL<br/>AUTH_HOST<br/>JWT_SECRET<br/>STRIPE_KEY<br/>fromGroup: shared"]
  end

  db -->|"fromDatabase"| apiEnvs
  kv -->|"fromService keyvalue"| apiEnvs
  psv -->|"fromService pserv"| apiEnvs
  grp -->|"fromGroup"| apiEnvs
  rnd -->|"generateValue"| apiEnvs
  user -->|"sync: false"| apiEnvs

fromDatabase - Postgres connection details

render.yaml
envVars:
- key: DATABASE_URL
fromDatabase:
name: my-postgres
property: connectionString

name matches a database in databases: in the same Blueprint (or, when you reach for them, in the same projects[].environments[] block). The available properties are:

propertyValue
connectionStringFull postgres://user:pass@host:port/db URL - what you’ll use 90% of the time
hostHostname only
portPort number
userDatabase user
passwordDatabase password
databaseDatabase name

Pull discrete fields when your driver wants them split (some Java/Go libraries do); pull connectionString everywhere else.

fromService - Key Value (Redis-compatible)

render.yaml
envVars:
- key: REDIS_URL
fromService:
name: my-cache
type: keyvalue
property: connectionString

Note type: keyvalue - that’s the modern spelling. The alias redis still works but you should prefer keyvalue in new files.

Available properties for Key Value:

propertyValue
connectionStringredis://... URL
hostInternal hostname
portPort number
hostporthost:port combined - handy for libraries that take them as one string

fromService - private and web services

render.yaml
envVars:
- key: AUTH_HOST
fromService:
name: auth
type: pserv
property: host
- key: AUTH_URL
fromService:
name: auth
type: pserv
property: hostport
- key: SHARED_SECRET
fromService:
name: auth
type: pserv
envVarKey: API_SECRET

The first two pull network coordinates. The third - envVarKey - is the trick most teams discover late: you can copy any env var from another service. A private service generates a token via generateValue, the web service pulls it across with envVarKey, and the secret never leaves Render.

property (pserv / web)Value
hostInternal hostname
hostporthost:port combined
envVarKeyCopy a named env var from the target service

fromGroup - pull a bundle

render.yaml
envVars:
- fromGroup: shared-config

No key - the line attaches every var in the named group. Covered in depth in the previous step.

generateValue - let Render mint a secret

render.yaml
envVars:
- key: JWT_SECRET
generateValue: true
- key: CRON_TOKEN
generateValue: true

Render generates a random base64-encoded 256-bit value once, on initial Blueprint sync, and persists it. Re-syncing the Blueprint does not regenerate the value - that would invalidate every signed token in flight.

Use it for any secret your app generates and consumes only itself: JWT signing keys, CSRF salts, internal cron auth tokens.

sync: false - defer to the Render Dashboard

render.yaml
envVars:
- key: STRIPE_API_KEY
sync: false
- key: OPENAI_API_KEY
sync: false

sync: false means “Render, don’t try to manage this from the Blueprint - I’ll fill it in on the Render Dashboard.” On the first Blueprint sync, Render prompts you for each sync: false key. On subsequent syncs, the existing value is preserved.

Three rules worth memorizing:

  • First sync prompts; later syncs don’t. Adding a new sync: false var to an existing Blueprint won’t re-prompt - set it in the Render Dashboard yourself.
  • sync: false is excluded from preview environments. Each preview spins up without those values; cover that in the previews step.
  • sync: false is silently invalid inside envVarGroups. Use it on a service or pre-fill the group’s values in the Render Dashboard.

Common combinations

A few patterns show up over and over.

Workflow trigger from a cron

render.yaml - cron triggering a workflow
- type: cron
name: daily-trigger
runtime: python
schedule: "0 14 * * *"
envVars:
- key: CRON_SECRET
generateValue: true
- key: WORKFLOW_SLUG
sync: false

The cron generates its own auth token (generateValue: true) and holds a WORKFLOW_SLUG that only exists after the workflow service is provisioned (sync: false). This is the same pattern used in the SF Pulse deploy tutorial - the Blueprint can’t fill in a slug it doesn’t know yet, so it punts to the Render Dashboard.

Sharing a database across services

render.yaml
databases:
- name: app-db
plan: basic-256mb
services:
- type: web
name: api
runtime: node
plan: starter
buildCommand: npm ci && npm run build
startCommand: npm start
envVars:
- key: DATABASE_URL
fromDatabase:
name: app-db
property: connectionString
- type: worker
name: jobs
runtime: node
plan: starter
buildCommand: npm ci
startCommand: node worker.js
envVars:
- key: DATABASE_URL
fromDatabase:
name: app-db
property: connectionString

Two services, one database. The wiring is independent - each service declares its own fromDatabase block. Don’t try to “DRY this up” with anchors; YAML anchors don’t compose with Render’s reconciler.

A web service wired to everything

render.yaml - composed wiring
services:
- type: web
name: api
runtime: node
plan: starter
buildCommand: npm ci && npm run build
startCommand: npm start
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
- key: STRIPE_API_KEY
sync: false
- fromGroup: shared-config

Six entries, six different patterns. Each one has exactly one source - that’s the rule.

The mistake that breaks the most syncs

A useful habit: keep a single naming table at the top of your render.yaml as a comment, and keep all wiring references in sync with it.

render.yaml
# Resource names (keep wiring references in sync):
# db: app-db
# cache: app-cache
# auth: app-auth
# web: api
# worker: jobs
You're wiring an env var to point at a Key Value instance and a teammate's PR also wires a private service. Which `property` value works for both?

What you learned

  • Six wiring primitives - `value`, `generateValue`, `sync: false`, `fromDatabase`, `fromService`, `fromGroup` - and each entry uses exactly one
  • `fromDatabase` and `fromService` (`keyvalue`) expose a full `connectionString`; `fromService` (`pserv`/`web`) exposes only `host` / `hostport` / `envVarKey`
  • `generateValue` mints a one-time random secret on first sync; `sync: false` defers to the Render Dashboard and is excluded from preview environments
  • Validate with `render blueprints validate` to catch typo'd `name:` references before they break a sync