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:
| Pattern | What it produces | When to use |
|---|---|---|
value | Inline literal | Non-secret config you’re happy committing |
generateValue | Render-generated random secret | App-internal secrets (JWT, CRON token) |
sync: false | Render prompts at sync time | Third-party API keys (Stripe, OpenAI) |
fromDatabase | DB connection string or part | Postgres credentials |
fromService | Another service’s host, port, or env var | Key Value, private services, web services |
fromGroup | Bundle of vars from an envVarGroup | Shared 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
envVars: - key: DATABASE_URL fromDatabase: name: my-postgres property: connectionStringname 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:
property | Value |
|---|---|
connectionString | Full postgres://user:pass@host:port/db URL - what you’ll use 90% of the time |
host | Hostname only |
port | Port number |
user | Database user |
password | Database password |
database | Database name |
Pull discrete fields when your driver wants them split (some Java/Go libraries do); pull connectionString everywhere else.
fromService - Key Value (Redis-compatible)
envVars: - key: REDIS_URL fromService: name: my-cache type: keyvalue property: connectionStringNote 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:
property | Value |
|---|---|
connectionString | redis://... URL |
host | Internal hostname |
port | Port number |
hostport | host:port combined - handy for libraries that take them as one string |
fromService - private and web services
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_SECRETThe 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 |
|---|---|
host | Internal hostname |
hostport | host:port combined |
envVarKey | Copy a named env var from the target service |
fromGroup - pull a bundle
envVars: - fromGroup: shared-configNo key - the line attaches every var in the named group. Covered in depth in the previous step.
generateValue - let Render mint a secret
envVars: - key: JWT_SECRET generateValue: true - key: CRON_TOKEN generateValue: trueRender 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
envVars: - key: STRIPE_API_KEY sync: false - key: OPENAI_API_KEY sync: falsesync: 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: falsevar to an existing Blueprint won’t re-prompt - set it in the Render Dashboard yourself. sync: falseis excluded from preview environments. Each preview spins up without those values; cover that in the previews step.sync: falseis silently invalid insideenvVarGroups. 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
- type: cron name: daily-trigger runtime: python schedule: "0 14 * * *" envVars: - key: CRON_SECRET generateValue: true - key: WORKFLOW_SLUG sync: falseThe 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
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: connectionStringTwo 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
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-configSix 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.
# Resource names (keep wiring references in sync):# db: app-db# cache: app-cache# auth: app-auth# web: api# worker: jobsWhat 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