Safeguard
Security Guides•

PostgreSQL public schema: who can create objects, and how to lock it

Upgraded PostgreSQL 14 or earlier databases let everyone create objects in the public schema. See how to check, revoke, and use a secure schema pattern.

Safeguard Security Team
3 min read

In databases upgraded from PostgreSQL 14 or earlier, every user can create objects in the public schema. PostgreSQL 15 and later defaults support a safer pattern. Run REVOKE CREATE ON SCHEMA public FROM PUBLIC; on older or upgraded databases, then give trusted roles the access they need.

What the public schema is

Every new database contains a schema named public. An object created without a schema name goes there, so CREATE TABLE products means CREATE TABLE public.products. By default everyone has USAGE on public, which means they can look things up in it.

The risk is the CREATE privilege. The manual says that in databases upgraded from PostgreSQL 14 or earlier, everyone has it on public. Anyone with a login can then add objects that other users' queries may pick up.

Why this matters

When a schema is on the search_path, you effectively trust everyone who can create objects in it. The manual states that adding a schema to the path "effectively trusts all users having CREATE privilege on that schema." A user could create a function or table with a name that shadows one another user expects, changing what that user's query does.

The fix

REVOKE CREATE ON SCHEMA public FROM PUBLIC;

The first public is the schema. The second means every user. The manual also advises auditing public for objects that are named like objects in pg_catalog.

Secure patterns the manual describes

  1. User-private schemas. Make sure no schema has public CREATE, then give each user a schema with their own name, for example CREATE SCHEMA alice AUTHORIZATION alice;. The default search path starts with "$user", so each user lands in their own schema. This is secure unless an untrusted user owns the database or holds ADMIN OPTION on a relevant role.
  2. Remove public from the search path, for example with ALTER ROLE ALL SET search_path = "$user";, then grant CREATE on public only where needed. The manual warns that calls to functions in public become unsafe or unreliable under this pattern, so prefer pattern 1 if you keep functions or extensions there.

The manual calls the third option, default path plus CREATE on public for everyone, "never a secure pattern." It is acceptable only with a single user or a few mutually trusting ones.

How to check your own database

Inspect the schema privileges with \dn+ in psql and confirm whether PUBLIC holds CREATE. Check the psql documentation for how to read the privilege column. Do this for every database, because the setting is per database. Then review who owns the database, since that role stays powerful under every pattern. Related reading: least privilege with predefined roles.

Quick check

Does PostgreSQL 15 fix this for databases I upgraded?

Not automatically. The manual says upgraded databases from 14 or earlier keep the open CREATE privilege until you revoke it.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.