Render Tutorials
Postgres on Render: a deep dive

Internal vs external connection strings

⏱ 7 min

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
URLWhen to useTLSIP allowlist
InternalA Render service in the same region and workspace as the databaseNot requiredDoesn’t apply
ExternalLocal dev, CI, BI tools, anything outside RenderRequired (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:

  1. Open the Render Dashboard dashboard.render.com.
  2. Select your database from the sidebar It’ll be under your workspace or project.
  3. Click the Connect tab Right next to Settings.
  4. 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:

sslmodeWhat it doesWhen to use
disableNo TLSNever on external - Render rejects the connection
requireTLS, no certificate verificationThe default Render docs recommend; works for most clients
verify-caTLS + verify the CA chainWhen your security policy requires it
verify-fullTLS + verify CA + verify hostnameStrictest; 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/0 is 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.

A Render web service is using `process.env.DATABASE_URL`, which someone pasted from the Render Dashboard's `External Database URL` field. The app works, but query latency is mysteriously 30-50ms higher than you'd expect for an in-region database. What's the most likely cause?

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