Every Render Postgres instance exposes two connection strings. They point at the same data, but they take different network paths and follow different rules.
Picking the right one is the most common Postgres-related mistake on Render. The good news: it’s also the easiest to fix once you know what you’re looking at.
The two URLs
flowchart LR
laptop["Laptop / CI / BI tool"]
service["Render service<br/>(same region)"]
edge["Public internet"]
pg[("Postgres instance")]
laptop -->|"External URL<br/>TLS required"| edge
edge --> pg
service -->|"Internal URL<br/>private network"| pg
| URL | When to use | TLS | IP allowlist |
|---|---|---|---|
| Internal | A Render service in the same region and workspace as the database | Not required | Doesn’t apply |
| External | Local dev, CI, BI tools, anything outside Render | Required (TLS 1.2+, sslmode=require) | Applies |
Both URLs follow the same Postgres-standard format:
- Internal:
postgres://user:password@hostname/dbname - External:
postgres://user:password@hostname/dbname?sslmode=require
The host portions differ - the internal hostname resolves only inside Render’s network - and the external URL adds the ?sslmode=require query parameter.
Where to find them
Both URLs live on the database’s Connect tab in the Render Dashboard:
- Open the Render Dashboard dashboard.render.com.
- Select your database from the sidebar It’ll be under your workspace or project.
- Click the Connect tab Right next to Settings.
- Copy the URL you need Internal for Render services in the same region; External for everything else.
In a Blueprint, you don’t paste URLs - fromDatabase always resolves the internal URL when both the consumer and the database live on Render. We cover that in the next step.
Pick the internal URL on Render. Always.
flowchart LR
app["Your service on Render"]
internal["Internal URL"]
external["External URL"]
pg[("Postgres")]
app -->|"low latency<br/>no public egress<br/>no TLS overhead"| internal
app -.->|"unnecessary public hop<br/>TLS handshake every conn<br/>extra latency"| external
internal --> pg
external --> pg
The single most common Postgres complaint on Render - “queries are slow / latency is weird / something feels off” - comes from a Render service mistakenly using the external URL.
What goes wrong:
- Latency. The traffic leaves Render’s network, hits the public internet, and comes back in. That’s tens of ms per query, on every query.
- TLS handshake on every connection. External requires TLS, internal doesn’t. With short-lived connections, the handshake adds up fast.
- Public egress patterns. Public traffic counts differently from private traffic - there’s no good reason to pay for it when the same data is available privately.
- IP allowlist surface. Your service’s IP isn’t fixed; switching to external means widening the allowlist or fighting it in CI.
The fix is one line - usually the value of DATABASE_URL on the consuming service. If you copy-pasted the URL out of the Render Dashboard while debugging, you almost certainly grabbed the external one. Swap to the internal URL or, better, use fromDatabase.
When the external URL is right
Three legitimate reasons:
- Local development. Your laptop has to use the external URL because it’s outside Render’s network. Use TLS, lock down the IP allowlist to your office or VPN.
- CI runners outside Render. GitHub Actions, GitLab CI, etc. running migrations or seed scripts. Same TLS rules; consider scoping the allowlist to the CI provider’s IP ranges.
- External tooling. BI dashboards (Metabase, Looker, hosted Hex), backup scripts on a separate VPS, observability tools. All external.
For each of these, the URL is one of many secrets you’d inject into the runtime - never committed to Git.
TLS and sslmode quick reference
The sslmode query parameter on the external URL controls how strictly the client verifies the database’s certificate. The minimum Render requires is require. Stricter modes work too if your client supports them:
sslmode | What it does | When to use |
|---|---|---|
disable | No TLS | Never on external - Render rejects the connection |
require | TLS, no certificate verification | The default Render docs recommend; works for most clients |
verify-ca | TLS + verify the CA chain | When your security policy requires it |
verify-full | TLS + verify CA + verify hostname | Strictest; requires you to ship Render’s CA bundle |
For internal connections, you don’t set sslmode at all - the connection happens on a private network where TLS would add overhead without benefit.
IP allowlist: the external-only knob
Render exposes a per-database IP allowlist (Dashboard → database → Access Control). It controls only external connections. Same-region Render services using the internal URL bypass it entirely.
flowchart LR
ext["External request"]
allow{"IP in allowlist?"}
yes["Connection accepted"]
no["Connection rejected"]
internal["Render service,<br/>internal URL"]
always["Always allowed"]
ext --> allow
allow -->|"yes"| yes
allow -->|"no"| no
internal --> always
Sensible defaults:
- Production: lock down to your office VPN, your CI provider’s IP ranges, and any BI/observability tools you operate. Don’t leave it
0.0.0.0/0(“everywhere”) for production. - Development:
0.0.0.0/0is fine for hobby projects; tighten it the moment you put real data in.
Adding an IP range is a Render Dashboard operation. There’s no Blueprint field for it on databases - managing the allowlist via UI is the supported path.
A quick decision diagram
flowchart TB q["Where am I connecting from?"] render["Render service in the<br/>same region as the database"] outside["Anywhere else"] internal["Internal URL<br/>(no TLS, no allowlist)"] external["External URL<br/>(?sslmode=require, allowlist applies)"] q -->|"Render-hosted"| render q -->|"laptop, CI, BI tools"| outside render --> internal outside --> external
If you can’t answer that question with certainty, the answer’s almost always “external” - Render services pull the internal URL automatically when you wire them with fromDatabase, which is what the next step covers.
What you learned
- One database, two URLs: internal (private network, no TLS) and external (public, TLS 1.2+ required)
- Render-hosted services should always use the internal URL - wrong URL = unnecessary latency and public egress
- TLS via `?sslmode=require` is the minimum for external; stricter modes (`verify-ca`, `verify-full`) work for tighter security policies
- IP allowlist applies only to external connections; same-region Render services bypass it on internal URLs
- If you can't tell which one a caller is - it's almost certainly external. Render services should use `fromDatabase` so the answer is automatic