What PgBouncer 1.26.1 fixes
On October 14, 2026, the PgBouncer project released version 1.26.1, which fixes three flaws. The most serious, CVE-2026-107350, is an authentication bypass: a client that knows the password of one SCRAM user can log in as any other SCRAM user, including PgBouncer administrators and PostgreSQL superusers. The mechanism is a second startup message, the StartupMessage, processed during SCRAM authentication when the connection targets a non-existent database. According to Mend's description, the SCRAM exchange is still verified against the keys of the first user, while the session opens under the identity of the second. The changelog states that the attack requires SCRAM for both users.
The other two flaws are denial-of-service issues, triggerable without authentication but only under certain configuration options: CVE-2026-107349 and CVE-2026-107348. Mend gives CVE-2026-107350 a CVSS v4 score of 8.7. The score matters less than a practical question: which version runs on your machines, and who can reach port 6432?
Affected versions and package status
Scroll the table
| Case | Status | Action |
|---|---|---|
| PgBouncer 1.17.0 to 1.26.0 | Affected by CVE-2026-107350 when both accounts use SCRAM | Upgrade to 1.26.1 |
| PgBouncer 1.20.0 to 1.26.0 | Affected by CVE-2026-107349 if `ignore_startup_parameters` is non-empty without `options` | Upgrade to 1.26.1 |
| PgBouncer 1.21.0 to 1.26.0 | Affected by CVE-2026-107348 only if `max_packet_size` exceeds 2147483648 | Upgrade to 1.26.1 |
| Debian bookworm, package 1.18.0-1+deb12u1 | Listed as vulnerable by the Debian security tracker | PGDG repository or build from source |
| Debian trixie, package 1.24.1-1+deb13u2 | Listed as vulnerable by the Debian security tracker | PGDG repository or build from source |
| Debian forky and sid, package 1.26.1-1 | Fixed | Upgrade through apt |
| Third-party Docker image | Depends on the embedded version | Check `pgbouncer -V` inside the container |
| Version older than 1.17.0 | Outside the range announced for CVE-2026-107350 | Do not settle for it, plan the upgrade |
"My PgBouncer is not exposed": an insufficient criterion
The objection is fair: a PgBouncer listening only on a private address cannot be reached from the Internet. But the attack needs no brute force. It is enough to know the password of one valid SCRAM user and to reach the port. An application password lives in a .env file, a CI variable, a developer workstation, sometimes a log. A compromised application provides both the password and the network position. From there, the flaw turns a limited application account into an administration account, or even a superuser on the PostgreSQL server behind it.
The deciding criterion is therefore not "Internet or not" but two questions: who can open a connection to port 6432, and how many valid passwords are in circulation. A vulnerable version behind a strict firewall is a reduced risk, not a removed one.
What determines your real exposure
- Listen address —
listen_addrhas no default value: without it, PgBouncer only accepts Unix sockets. The value*opens every interface. - Authentication type — the attack requires SCRAM for both users. Look at
auth_typeand at the nature of the entries in the file named byauth_file. - Number of accounts — every valid password is an entry point to all the other SCRAM accounts. Ten application accounts are ten keys to the same door.
- Administration accounts —
admin_usersopens the console, and the role used to reach PostgreSQL may be a superuser. - Docker and firewall — a port published by Docker goes through its own network rules, which often bypass ufw. Check how the port is published, not only the firewall.
- Options tied to the denial-of-service flaws —
ignore_startup_parametersnon-empty withoutoptions, ormax_packet_sizeraised above its default. - Containers and bundled stacks — a PgBouncer shipped in a Compose stack or a chart keeps its image's version, whatever the host server runs.
Check the version and exposure in a few minutes
Query the admin console
psql -h 127.0.0.1 -p 6432 -U <admin_user> pgbouncer -c "SHOW VERSION;"
Only users listed inadmin_usersorstats_userscan connect. TheSHOW VERSIONcommand prints PgBouncer's version string: after the fix, it must mention 1.26.1.Read the binary version
pgbouncer -Vwhich -a pgbouncer
Several binaries sometimes coexist, the package one and another under/usr/localafter a build. The first in the list is not always the one the service runs.Read the package version
Debian and Ubuntu:
dpkg -l pgbouncerapt-cache policy pgbouncer
RHEL, AlmaLinux, Rocky:rpm -q pgbouncerdnf info pgbouncer
Compare with the table above. A distribution package can receive a fix without changing its upstream version number: trust the distribution's security tracker more than the number alone.Reread the configuration actually loaded
ps -eo pid,args | grep '[p]gbouncer'grep -nE '^(listen_addr|listen_port|auth_type|auth_file|admin_users|ignore_startup_parameters|max_packet_size)' /etc/pgbouncer/pgbouncer.ini
The.inifile to read is the one passed as an argument to the process, not necessarily the usual path.See who can reach the port
sudo ss -ltnp | grep 6432sudo ufw status numberednc -vz <server_ip> 6432
The last command, run from another machine, tells you whether the port answers outside the intended network. An unexpected answer calls for an immediate firewall decision.Count SCRAM accounts without printing secrets
grep -c 'SCRAM-SHA-256' /etc/pgbouncer/userlist.txt
Replace the path with that ofauth_file. Never paste the file contents into a ticket or a chat: it holds the fingerprints of your passwords.Inventory the containers
docker psdocker port <container>docker exec <container> pgbouncer -V
A running container keeps the image it started with. Thedocker portoutput says whether the port is published on the host.
Which upgrade route: the state of Debian packages
At the time of writing, the Debian security tracker lists bookworm (1.18.0-1+deb12u1) and trixie (1.24.1-1+deb13u2) as vulnerable to CVE-2026-107350, with only forky and sid carrying the fixed package 1.26.1-1. Until Debian publishes a fix for your release, apt upgrade alone does not protect you. Recheck the tracker on the day you act: the state may have changed.
Three routes exist. The first: the PGDG repository, apt.postgresql.org for Debian and Ubuntu, yum.postgresql.org for RHEL and derivatives, which the PgBouncer download page describes as builds that may be newer than those in distributions. The second: an up-to-date Docker image or tag, knowing that the project mentions no official image, so you are using a third-party one. The third: build the source archive of version 1.26.1. Before switching, confirm with apt-cache policy pgbouncer or dnf info pgbouncer that the repository really offers the fixed version.
Upgrade according to your installation method
Back up the configuration
sudo cp -a /etc/pgbouncer /root/pgbouncer-etc-backup
This copy lets you roll back if an upgrade replaces a file. Also note the current version before you start.Debian and Ubuntu: enable the PGDG repository
sudo apt install -y postgresql-common ca-certificatessudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.shapt-cache policy pgbouncer
The official script configures the apt.postgresql.org repository. The last command must show a 1.26.1 candidate; if it is missing, go no further with this route.Debian and Ubuntu: install
sudo apt updatesudo apt install --only-upgrade pgbouncerpgbouncer -V
If dpkg offers to replace your configuration file, keep your local version.pgbouncer -Vconfirms the installed binary.RHEL, AlmaLinux and Rocky
Add the PGDG repository with the commands generated by the yum.postgresql.org selector for your distribution, then:
sudo dnf makecachednf info pgbouncersudo dnf upgrade pgbouncer
Check that the proposed version is 1.26.1 before confirming.Docker and Compose
docker compose pull pgbouncerdocker compose up -d pgbouncerdocker compose exec pgbouncer pgbouncer -V
If the image is pinned to an old tag or digest, first update that reference in your Compose file. If no 1.26.1 tag exists, build the image from source.Build from source
curl -LO https://www.pgbouncer.org/downloads/files/1.26.1/pgbouncer-1.26.1.tar.gzsha256sum pgbouncer-1.26.1.tar.gz
Compare the checksum you get with the one published on the download page before continuing.tar xzf pgbouncer-1.26.1.tar.gzcd pgbouncer-1.26.1./configure --prefix=/usr/localmakesudo make install
The build needs libevent 2.0 or later, OpenSSL 1.0.1 or later and a C11 compiler. The documentation recommends Autoconf for production.Restart, then verify
sudo systemctl restart pgbouncerps -o lstart= -p $(pgrep -o pgbouncer)sudo readlink -f /proc/$(pgrep -o pgbouncer)/exe
A reload is not enough: the process must restart on the new binary. The start time and the executable path prove it. Finally, runSHOW VERSIONon the console again.
Restarting without cutting clients, and the search_path case
A restart drops open connections. The documentation describes an alternative: several processes on the same port with so_reuseport, then SHUTDOWN WAIT_FOR_CLIENTS on one process at a time while clients reconnect. Another point of attention: since version 1.26.0, search_path is tracked by default, and 1.26.1 makes ignore_startup_parameters take precedence over tracked parameters. If an application sent search_path at startup and you were counting on ignoring it, add ignore_startup_parameters = search_path to the configuration to get the 1.25 behavior back.
Workarounds until the fix is in
For CVE-2026-107350, the announcement gives no configuration workaround: the upgrade is the only real answer. You can, however, reduce who gets to try. Limit port 6432 to the application network:sudo ufw allow from <app_subnet> to any port 6432 proto tcpsudo ufw status numberedsudo ufw delete <n>
Here <n> is the number of a broader rule already present, for example one opening port 6432 to everyone, which you delete once the restricted rule is in place. On RHEL and derivatives, the same rule is set with firewalld. If listen_addr is *, replace it with the server's private address. Then audit the auth_file file: remove unused accounts, rotate passwords that have circulated, and shrink admin_users to the bare minimum.
For the two denial-of-service flaws, what follows is our reading of the changelog's conditions, not a vendor instruction. CVE-2026-107349 assumes ignore_startup_parameters is non-empty and lacks options: adding options to the list removes the condition; emptying the setting also removes it, but may cause clients that send startup parameters to be rejected (see the search_path case above). CVE-2026-107348 assumes a max_packet_size above 2147483648: keeping the default value, 2147483647, rules it out.
Review the traces of an impersonated connection
By default PgBouncer logs successful connections and disconnections, with their reason, thanks to log_connections and log_disconnections. Read the period that follows the publication of the fixes, and further back if your logs allow:sudo journalctl -u pgbouncer --since "2026-10-14"
Look for attempts against database names that do not exist in your [databases] section, then a session opened just afterwards under a different user name from the same address. On the console, these two commands show the current state:psql -h 127.0.0.1 -p 6432 -U <admin_user> pgbouncer -c "SHOW DATABASES;"psql -h 127.0.0.1 -p 6432 -U <admin_user> pgbouncer -c "SHOW CLIENTS;"
On the PostgreSQL side, list the roles and their recent changes with psql -c "\du".
We do not have an exact signature of this attack in the logs, and we do not invent one. An absence of traces does not prove an absence of intrusion, especially if log_connections was turned off or logs have rotated. If you have serious doubts, change the passwords of all SCRAM accounts and of the privileged roles on the PostgreSQL server.
Closing the file: from version to follow-up
A successful upgrade is proven, not assumed. For each server, record in a table the machine, the installation method, the version before and after, the firewall rule applied, and the date of the check. Verify that SHOW VERSION reports 1.26.1, that the process restarted after the installation, and that port 6432 is reachable only from the intended network. Also confirm that your two denial-of-service settings respect the conditions described above, or that they have become moot.
Finally, do not count on automatic updates: an unfixed distribution package does not update itself, and neither does a pinned Docker image. Add a reminder to recheck your distribution's security tracker, then switch to its package once it is fixed, or keep the PGDG repository deliberately.
On a VPS of your own, you hold the three levers
Fixing PgBouncer takes three kinds of access: choosing the installed version, setting the firewall on port 6432 and reading the logs. On a VPS Cloud with root access, these three levers are yours, without depending on a shared host's schedule. The guides linked below this article detail the firewall and the PostgreSQL setup. This guide is provided without support: the upgrade remains your responsibility. If you would rather delegate server administration, the VPS administration option exists, to be assessed against your needs.
The official documentation
Announcement of version 1.26.1: https://www.postgresql.org/about/news/pgbouncer-1261-released-fixes-three-cves-3403/
PgBouncer changelog: https://www.pgbouncer.org/changelog.html
PgBouncer downloads: https://www.pgbouncer.org/downloads/
Usage and admin console: https://www.pgbouncer.org/usage.html
PgBouncer configuration: https://www.pgbouncer.org/config.html
Debian security tracker for CVE-2026-107350: https://security-tracker.debian.org/tracker/CVE-2026-107350
Mend entry for CVE-2026-107350: https://www.mend.io/vulnerability-database/CVE-2026-107350/
PostgreSQL apt repository: https://wiki.postgresql.org/wiki/Apt
PostgreSQL yum repository: https://yum.postgresql.org/
Docker and firewalls: https://docs.docker.com/engine/network/packet-filtering-firewalls/