Blog
5 min read

Postgres Connection Strings Explained: Format, Examples, and Common Errors

What every part of a PostgreSQL connection string (DATABASE_URL) means, how to write one, special characters in passwords, sslmode options, pooled vs direct connections (including Supabase's ports), and how to fix the errors people hit most.

Most apps connect to Postgres with a single line in an environment variable, usually called DATABASE_URL:

postgresql://app_user:s3cret@db.example.com:5432/myapp?sslmode=require

That's a connection string (or connection URI). It packs everything your app needs to find and log in to the database into one URL. Here's what each piece means and how to fix it when it doesn't work.

The parts

postgresql://  app_user  :  s3cret  @  db.example.com  :  5432  /  myapp  ?  sslmode=require
   scheme        user       password       host            port     database     options
Part Meaning
postgresql:// The scheme. postgres:// also works with most tools.
app_user The database user (role) to log in as.
s3cret That user's password.
db.example.com The server's hostname or IP address. localhost for a database on the same machine.
5432 The port. 5432 is Postgres's default and can be omitted. (Ports explained.)
myapp The database name on that server.
?sslmode=require Options, like a URL query string.

(It follows the same structure as a web address — see anatomy of a URL.)

Special characters in the password

If the password contains characters that mean something in a URL — @, :, /, ?, #, % — they must be percent-encoded, or the string is parsed wrongly:

Character Encoded
@ %40
: %3A
/ %2F
# %23
? %3F
% %25

So a password of p@ss#1 becomes p%40ss%231. Symptoms of getting this wrong: errors mentioning an unexpected host, or "password authentication failed" even though the password is right. The simplest fix for new databases: generate passwords with only letters and numbers (and make them long).

sslmode: encryption to the database

sslmode controls whether the connection is encrypted:

Value Meaning
disable No encryption. Only for a database on the same machine or a private network.
prefer Use encryption if the server offers it (a common default).
require Always encrypt; don't verify the server's certificate.
verify-full Encrypt and verify the certificate and hostname — the most secure.

For a database reached over the internet, use at least require, ideally verify-full. Many hosted providers require SSL and will refuse unencrypted connections.

Pooled vs direct connections

Many hosts give you two connection strings:

  • a direct connection to Postgres, and
  • a pooled connection through a connection pooler (like PgBouncer), often on a different port.

Supabase, for example, uses port 5432 for direct and session-mode pooled connections and 6543 for its transaction-mode pooler. Use the pooled string for apps that open many short-lived connections (especially serverless functions), and the direct one for running migrations and admin tasks — some migration tools don't work through a transaction-mode pooler. See Postgres connection pooling.

Keep it secret

A connection string contains a password with access to all your data. Treat it like any secret:

  • keep it in an environment variable, never in code (environment variables explained),
  • never put it in frontend code or a VITE_/NEXT_PUBLIC_ variable,
  • make sure .env is in .gitignore,
  • use a database user with only the permissions your app needs.

Common errors and what they mean

Error Usual cause
password authentication failed for user Wrong password, wrong user, or an unencoded special character
connection refused / ECONNREFUSED Nothing listening at that host and port — wrong host/port, database not running, or a firewall
ENOTFOUND / could not translate host name Hostname typo, or a hostname that only works inside a private network (like a Docker service name used from your laptop)
database "myapp" does not exist Wrong database name, or it hasn't been created yet
no pg_hba.conf entry for host / SSL required The server requires SSL — add sslmode=require
too many connections / remaining connection slots are reserved Too many open connections — use the pooled string or reduce your pool size
self-signed certificate / certificate verify failed Using verify-full without the provider's CA certificate — supply it, or use require

A classic one: the app works locally but not in production because DATABASE_URL still says localhost. On the server, localhost means the server itself. See why does my app work locally but not in production?

Testing a connection string

With psql installed:

psql "postgresql://app_user:s3cret@db.example.com:5432/myapp?sslmode=require"

If you get a myapp=> prompt, the string works, and the problem is in how your app reads it. Type \q to quit. More ways to look inside: how to view your Postgres database.

The summary

  • A connection string is postgresql://user:password@host:port/database?options.
  • Percent-encode special characters in passwords.
  • Use sslmode=require or verify-full over the internet.
  • Use pooled strings for the app, direct ones for migrations.
  • Keep it in a server-side environment variable.

EasySpawn provisions PostgreSQL on every server and hands your app its connection details as DATABASE_URL automatically — no copying passwords between dashboards. See how it works or join the waitlist.

Related: What Is a Database? · Postgres vs MySQL · Docker Compose for Local Development · What Is an ORM? · ECONNREFUSED 127.0.0.1:5432

Keep reading