---
title: "PgBouncer in transaction mode: Prisma, Django, Rails"
url: https://while.sh/en/guides/postgresql-pgbouncer/
author: "Aloïs Oddon, co-founder of Nextmap"
updated: 2026-11-02
---

# PostgreSQL behind PgBouncer: Prisma, Django, Rails and your migrations, with your AI agent

Your PostgreSQL database on while goes through PgBouncer, to handle hundreds of clients at once. Your AI agent knows what that changes for your code, and sets it up for you.

## Prerequisites

- An app deployed on while, with its PostgreSQL database.
- Your AI agent, connected to while over MCP.

## Step 1: Ask for a check

> You, to your agent: Check that our code works behind PgBouncer.

Your agent reviews your code and your database settings, and fixes anything that assumes it keeps the same database connection from one query to the next.

## Step 2: What needs a session runs separately

Anything that needs a full session, like `pg_restore` or a migration that runs `SET` commands, runs in a job, which your agent also starts on request. The job receives the database's direct address in `DB_MAIN_DIRECT_URL`.

## What your agent does

In transaction mode, each transaction can get a different connection. Prepared statements are tracked, so Prisma and node-postgres work with no configuration. A session `SET`, an advisory lock or a server-side cursor, however, doesn't carry over from one transaction to the next (source 1). To configure the session, your agent uses `SET LOCAL`, inside a transaction:

```js titre="server.js"
const client = await db.connect();
try {
  await client.query('BEGIN');
  await client.query("SET LOCAL statement_timeout = '5s'");
  await client.query('SELECT count(*) FROM commandes');
  await client.query('COMMIT');
} catch (e) {
  await client.query('ROLLBACK');
  throw e;
} finally {
  client.release();
}
```

## Set up Django

> You, to your agent: Set up Django for PgBouncer.

Your agent turns off server-side cursors:

```python titre="settings.py"
DATABASES["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
```

## Set up Rails

> You, to your agent: Set up Rails for PgBouncer.

Your agent turns off the migrations' advisory locks:

```yaml titre="config/database.yml"
production:
  primary:
    url: <%= ENV["DATABASE_URL"] %>
    advisory_locks: false
```

## Keep Prisma as is

> You, to your agent: Use Prisma for the migrations.

Your agent leaves Prisma on `DATABASE_URL`, migrations included, so there is nothing to change. It doesn't declare a `directUrl`, which Prisma would then demand at startup with error P1012, since the direct address is given only to jobs.

## Find the slow queries

> You, to your agent: Which queries are slowing down the database?

Your agent reads your database's most frequent and slowest queries, as recorded by pg_stat_statements.

## Next steps

- [Deploy a Node.js API and its PostgreSQL database](/en/guides/deploy-node-api-postgresql/)
- [Deploy a Django app](/en/guides/deploy-django/)
- [Restore your database to a precise moment](/en/guides/restore-postgresql/)

## Sources

1. [PgBouncer, what each pooling mode supports](https://www.pgbouncer.org/features.html), 2026-11-02

## Questions

### Do I need ?pgbouncer=true for Prisma?

No: PgBouncer tracks prepared statements, and the DATABASE_URL address works as is.

### Can I connect without PgBouncer?

A job can: it receives the direct address in DB_MAIN_DIRECT_URL. Your app always goes through PgBouncer.

### How many clients at the same time?

It depends on the database size: 200 on S, the default size, 400 on M, 600 on L, 800 on XL.
