The Blueprint primitive that connects a service to a database is fromDatabase. It pulls the connection details from the database resource at sync time and exposes them as env vars on the consuming service.
Done right, you never paste a connection string into your code or the Render Dashboard. Done badly, you end up with stale URLs, broken syncs, or worse - a DATABASE_URL in version control.
The basic pattern
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: connectionStringThree things happen at sync time:
- Render finds the database named
app-db(declared in the same Blueprint or already provisioned in the workspace). - It resolves
property: connectionStringto the internal URL -postgres://user:password@hostname/dbname. - It sets
DATABASE_URLon theapiservice to that value.
The service never sees the URL in YAML. The URL never lives in Git. Rotating credentials (e.g. through a database migration to a new instance) is a one-PR affair.
All the properties you can pull
envVars: - key: DATABASE_URL fromDatabase: name: app-db property: connectionString
- key: PGHOST fromDatabase: name: app-db property: host
- key: PGPORT fromDatabase: name: app-db property: port
- key: PGUSER fromDatabase: name: app-db property: user
- key: PGPASSWORD fromDatabase: name: app-db property: password
- key: PGDATABASE fromDatabase: name: app-db property: database| Property | Value |
|---|---|
connectionString | Full postgres://user:password@host:port/database URL |
host | Internal hostname only |
port | Database port (typically 5432) |
user | Postgres role |
password | Postgres password |
database | The actual database name (the path component) |
connectionString is what you’ll use 90% of the time. The discrete properties are useful when:
- Your driver wants
PGHOST,PGUSER, etc. as separate env vars (thelibpqconvention -psqlreads them automatically). - You’re constructing a non-Postgres-standard connection string (some Java drivers want a
jdbc:prefix; some Go drivers want key=value pairs). - You want to log the host or user in a startup banner without logging the password.
Multi-service consumers: web, worker, cron
A typical app has more than one service that talks to the database. Wire each one independently - declare a fromDatabase block for every consumer.
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
- type: cron name: nightly-cleanup runtime: node schedule: "0 3 * * *" buildCommand: npm ci startCommand: node cleanup.js envVars: - key: DATABASE_URL fromDatabase: name: app-db property: connectionStringThree services, one database, three independent fromDatabase blocks. Each service gets its own copy of DATABASE_URL resolved at sync time.
Splitting into a shared env group (advanced)
When you have many services pulling the same vars, an envVarGroup reduces the total YAML. The group can hold the fromDatabase block, and each service references the group:
envVarGroups: - name: db-config envVars: - key: DATABASE_URL fromDatabase: name: app-db property: connectionString
services: - type: web name: api runtime: node plan: starter envVars: - fromGroup: db-config
- type: worker name: jobs runtime: node plan: starter envVars: - fromGroup: db-configThis is the same env group pattern from the advanced Blueprint patterns tutorial - applied here to keep the database URL declared in one place.
Anatomy of the connection string
When you get into debugging - or you want to write a startup log line that doesn’t leak secrets - it helps to know what the URL parts mean.
postgres://USER:PASSWORD@HOST:PORT/DATABASE | | | | | | | | | | | +-- Database name (path) | | | | +-------- Port (default 5432) | | | +------------- Hostname (internal or external) | | +---------------------- Password | +---------------------------- User (Postgres role) +-------------------------------------- Scheme (postgres or postgresql)postgres:// and postgresql:// are interchangeable - both standard, every driver accepts both. The trailing ?sslmode=require (only on external URLs) is a query parameter the driver hands to libpq.
Multiple logical databases on one instance
A single Postgres instance can host multiple logical databases - same host, port, and user, but different DATABASE path components in the URL. Useful when you have multiple small apps or environments and don’t want to pay for separate Postgres instances.
- Connect to the primary database with `psql` Use the connection string from the Render Dashboard or
render psql <database-id>. - Run `CREATE DATABASE new_db;` Standard SQL. The new database inherits the role and host of the primary.
- Build a new connection string Take the existing URL and swap the path component for
new_db. Everything else stays the same. - Wire it into a service Use
fromDatabasefor the host/user/password parts, then construct the URL in code or a startup script. Or use the discrete properties (PGHOST,PGUSER, etc.) and let your app fill in the database name.
services: - type: web name: secondary-app runtime: node envVars: - key: PGHOST fromDatabase: name: app-db property: host - key: PGPORT fromDatabase: name: app-db property: port - key: PGUSER fromDatabase: name: app-db property: user - key: PGPASSWORD fromDatabase: name: app-db property: password - key: PGDATABASE value: new_db - key: DATABASE_URL value: postgres://$PGUSER:$PGPASSWORD@$PGHOST:$PGPORT/$PGDATABASEThis is genuinely useful for cost savings - three small apps sharing one basic-1gb instance is much cheaper than three separate basic-256mb instances. The tradeoffs:
- Connection limits are shared. All logical databases on the instance count against the same
max_connectionscap (covered in step 06). - Backups are at the instance level. Restoring a snapshot brings back every logical database on it.
- Resource contention is real. A heavy query in one logical database affects the others.
If those tradeoffs are uncomfortable, separate instances are the right call.
Validation: catch typos before they bite
The single most common fromDatabase mistake is a typo in the name: field - referencing a database that doesn’t exist.
services: - type: web name: api envVars: - key: DATABASE_URL fromDatabase: name: app-bd # typo: should be 'app-db' property: connectionStringThe schema doesn’t catch this - app-bd is a valid string. Semantic validation does:
render blueprints validate# Error: fromDatabase references unknown database 'app-bd'Run render blueprints validate before you commit any change to a fromDatabase block. The CLI is covered in the advanced Blueprint patterns introduction.
What you learned
- `fromDatabase` resolves the database's internal URL (or discrete fields) at sync time and injects them as env vars on the consuming service
- Use `connectionString` for most apps; pull discrete properties (`host`, `port`, `user`, `password`, `database`) when your driver wants them split
- Wire each consumer with its own `fromDatabase` block - don't try to DRY with YAML anchors. An `envVarGroup` is the right tool for shared wiring
- A single Postgres instance can hold multiple logical databases (`CREATE DATABASE`); they share host, credentials, connection caps, and backups
- `render blueprints validate` catches typos in the `name:` reference before they break a sync