I don’t understand why pgbouncer isn’t enabled by default

For context, I’m running mastodon within docker, with all services running on a single VM. This means that networking is fairly straight forward, and the usage of the system is small, so the complexity of it is also small. But something that I keep reading, is that sooner or later, you’ll start running out of postgresql connections as you start needing to scale up your sidekiq workers.

So why not nip that in the bud?

“The time to fix the roof is when the sun is shining.”

My server’s utilization is quite low, so why not take the time to configure pgbouncer to sit between sidekiq and all of the workers.

postgresql prep

I started by googling around, and came across this guide from 2018, and started adapting it for my docker setup. The first thing that I did was configure a password. The reason that this is necessary, is under a docker configuration, postgres just accepts connections from localhost, so it’s not necessary to set any auth, because we’re all cool here and we can all be trusted. Right? Well, not really.

I ran:

docker exec -it live_db_1 psql -U postgresql

to get into psql, followed by ALTER USER mastodon WITH PASSWORD 'secret'; to set the password (but I used something other than “secret”).

Installing pgbouncer

I googled around a little more, and came upon this small pgbouncer docker image, and filled in my docker-compose.yml with the following:

  pgbouncer:
    restart: always
    image: edoburu/pgbouncer
    networks:
      - internal_network
    depends_on:
      - db

running docker-compose up -d installed the image, and started the container, which immediately started crashlooping. I re-read the docs and started filling in the following environment variables:

  pgbouncer:
    restart: always
    image: edoburu/pgbouncer
    networks:
      - internal_network
    depends_on:
      - db
    environment:
      - 'DB_HOST=db'
      - 'DB_NAME=mastodon_production'
      - 'DB_USER=mastodon'
      - 'DB_PASSWORD=secret'

Ran docker-compose up -d, and then docker logs --follow live_pgbouncer_1 and saw that it started up cleanly. Hurrah!

I then added dependencies of pgbouncer to the other containers (which depended on db), and updated the .env.production file to point to pgbouncer instead of db. Ran docker-compose up -d once more, and everything was working again, but flowing through pgbouncer now.

Last tweaks

Added some max connections settings, and resulted in the following changes to my docker-compose.yml file (I keep daily backups of my configs, and I compared the changes with diff against yesterday’s version of the file):

diff -Naur /backup/dailies/docker-compose.2022-11-29.yml docker-compose.yml
--- /backup/dailies/docker-compose.2022-11-29.yml       2022-11-29 00:00:35.962626591 +0000
+++ docker-compose.yml  2022-11-30 06:29:13.308741138 +0000
@@ -13,6 +13,21 @@
     environment:
       - 'POSTGRES_HOST_AUTH_METHOD=trust'
 
+  pgbouncer:
+    restart: always
+    image: edoburu/pgbouncer
+    networks:
+      - internal_network
+    depends_on:
+      - db
+    environment:
+      - 'DB_HOST=db'
+      - 'DB_NAME=mastodon_production'
+      - 'DB_USER=mastodon'
+      - 'DB_PASSWORD=secret'
+      - 'MAX_CLIENT_CONN=100'
+      - 'DEFAULT_POOL_SIZE=20'
+
   redis:
     restart: always
     image: redis:7-alpine
@@ -69,7 +84,7 @@
     ports:
       - '127.0.0.1:3000:3000'
     depends_on:
-      - db
+      - pgbouncer
       - redis
       # - es
     environment:
@@ -95,7 +110,7 @@
     ports:
       - '127.0.0.1:4000:4000'
     depends_on:
-      - db
+      - pgbouncer
       - redis
 
   sidekiq:
@@ -105,7 +120,7 @@
     env_file: .env.production
     command: bundle exec sidekiq -c 25
     depends_on:
-      - db
+      - pgbouncer
       - redis
     networks:
       - external_network