RLS Tenant Isolation

Published
Length
3 min read

Luis Felipe Dias

All posts

You set up rls tenant isolation by adding row rules to shared database tables. The database checks a tenant ID on each row before it returns results. Many teams use shared tables to keep schema updates simple across hundreds of customers. When a database role has broad permissions, one bad query can leak customer data. Row rules move the tenant filter from app code into the database engine. How rls tenant isolation works in a shared database In a shared table design, every row holds a column

K8s Namespace Isolation: the complete guide

Architecture patterns, implementation strategies, and security best practices. Delivered to your inbox.

Free white paper. No spam. Unsubscribe anytime.

You set up rls tenant isolation by adding row rules to shared database tables. The database checks a tenant ID on each row before it returns results. Many teams use shared tables to keep schema updates simple across hundreds of customers. When a database role has broad permissions, one bad query can leak customer data. Row rules move the tenant filter from app code into the database engine.

How rls tenant isolation works in a shared database

In a shared table design, every row holds a column for the tenant ID. When an app starts a query, it sets the current tenant in the session. The database engine reads this session value before it scans or updates any rows. It appends a hidden filter to every read and write command on that table.

If a query asks for all records, the database returns only matching tenant rows. The app does not need to add a filter clause to every single query. If a developer forgets a filter in code, the database rule still blocks cross-tenant reads. Write rules also check that new rows carry the right tenant ID before saving.

This setup relies on a strict separation between the app role and admin roles. The app role must follow row rules on every table it touches. Database owners and superusers often bypass row rules by default unless you change table settings. Teams must check role flags to make sure the app role never skips these checks.

How connection pools handle tenant session state

Most web apps use a pool to share open database sessions across many requests. When a request arrives, the app grabs an open session and sets the tenant ID. If the app fails to reset that session, the next request keeps the old tenant. That mistake lets one customer read another customer's rows from the shared pool.

To prevent this leak, teams scope the tenant variable to a single transaction block. When the transaction ends, the database clears the tenant value right away. If a new query runs without setting a tenant, the row rule matches nothing. Returning zero rows by default is much safer than falling back to an older session value.

K8s Namespace Isolation: architecture patterns and best practices

Continue reading? Get the full guide.

Free. No spam. Unsubscribe anytime.

What native row rules solve and where gaps remain

Row rules solve the problem of missing tenant filters inside normal app queries. They enforce data boundaries at the storage tier when the app logs in. Still, native setup only governs the role that runs inside the database engine. It does not control how engineers or support staff run ad-hoc queries during incidents.

When an engineer logs in to fix a bug, they often use an admin role. Admin roles frequently bypass row rules or hold permissions to change session variables at will. Even if an engineer sets a tenant ID, native rules do not mask sensitive columns. Query results still return raw emails, phone numbers, card numbers, and national IDs in plain text.

Native rules also do not require peer approval before someone runs a risky delete command. The database checks who logged in, not whether a human reviewed the query in flight. Teams still need a way to govern human sessions and rewrite sensitive fields in results.

Check database and API queries on the wire

Identity tools, roles, and network rules decide who opens a session. Setup alone cannot check what each query does or mask sensitive values in a response once a session starts. When workflows touch Postgres, MySQL, SQL Server, MongoDB, ClickHouse, Kubernetes APIs, SSH, or internal HTTP APIs, hoop.dev runs beside those services as an open-source proxy, a sidecar for your Postgres or API. It inspects requests coming in and results going back on the wire.

Your login details and client tools stay as they are while hoop.dev guardrails refuse bad deletes or drops before they run. On responses, hoop.dev data masking rewrites matched sensitive values in memory. Furthermore, hoop.dev holds SOC 2 Type II, is MIT licensed, and writes every query and decision to an audit trail that generates evidence for HIPAA, GDPR, and PCI DSS. See the source on GitHub.

Frequently asked questions

Does rls tenant isolation hurt database query speed?

It can slow down reads if the tenant ID column lacks a proper index. Adding an index that starts with the tenant ID keeps row lookups fast.

Why did an admin query return rows for all tenants?

Table owners and superuser roles skip row rules by default in most database engines. You must test rules using a standard app role with no bypass flags.

What happens if an app query runs without setting the tenant ID?

If you write a strict rule, the database sees a blank value and returns zero rows. Always test that missing session variables fail closed instead of returning table data.

Save the open-source gateway.

hoop is MIT-licensed infrastructure for controlling how AI agents reach production data. Star hoophq/hoop so you can inspect it, deploy it, or share it when your team starts governing agent access.