{"id":941,"date":"2026-08-21T23:27:41","date_gmt":"2026-08-21T23:27:41","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=941"},"modified":"2026-08-26T22:04:22","modified_gmt":"2026-08-26T22:04:22","slug":"pgbouncer-connection-pooling-postgresql-vps","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/","title":{"rendered":"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers"},"content":{"rendered":"<p class=\"wp-block-paragraph\">PostgreSQL creates one OS process per connection, and each idle connection can consume 5\u201310 MB of RAM. On a small VPS with 2\u20134 GB of memory, an application that opens 200+ database connections will exhaust available RAM long before PostgreSQL does any real work. PgBouncer sits between your application and PostgreSQL, multiplexing hundreds of client connections through a small pool of server-side connections. This tutorial walks through installing, configuring, sizing, and securing PgBouncer on a typical Linux VPS.<\/p>\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n<p class=\"wp-block-paragraph\">Before you start, you need:<\/p>\n\n<ul class=\"wp-block-list\"><li>A Linux VPS (Ubuntu 22.04\/24.04 or Debian 12 recommended) with PostgreSQL already installed and running<\/li><li>Root or sudo access<\/li><li>PostgreSQL listening on localhost (default port 5432)<\/li><li>At least one database user and database to pool<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">If you haven&#8217;t yet chosen a VPS provider, <a href=\"https:\/\/virtualserversvps.com\/\">compare VPS plans on our comparison table<\/a> to find a server with enough RAM headroom for PostgreSQL plus PgBouncer.<\/p>\n\n<h2 class=\"wp-block-heading\">Step 1: Install PgBouncer<\/h2>\n\n<p class=\"wp-block-paragraph\">PgBouncer is available in the default repositories of all major distributions. Install it with:<\/p>\n\n<pre class=\"wp-block-code\"><code># Debian \/ Ubuntu\nsudo apt update\nsudo apt install pgbouncer -y\n\n# Verify installation\npgbouncer --version\n# Expected output: pgbouncer 1.xx.x<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">On RHEL\/AlmaLinux\/Rocky Linux, use <code>dnf install pgbouncer<\/code> instead. The EPEL repository may be required.<\/p>\n\n<h2 class=\"wp-block-heading\">Step 2: Configure pgbouncer.ini<\/h2>\n\n<p class=\"wp-block-paragraph\">The main configuration file lives at <code>\/etc\/pgbouncer\/pgbouncer.ini<\/code>. Open it and replace the contents with a minimal working setup:<\/p>\n\n<pre class=\"wp-block-code\"><code>[databases]\nmyapp = host=\/var\/run\/postgresql port=5432 dbname=myapp\n\n[pgbouncer]\nlisten_addr = 127.0.0.1\nlisten_port = 6432\nauth_type = md5\nauth_file = \/etc\/pgbouncer\/userlist.txt\npool_mode = transaction\ndefault_pool_size = 20\nmax_client_conn = 500\nreserve_pool_size = 5\nreserve_pool_timeout = 5\nserver_idle_timeout = 300<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Key settings explained:<\/p>\n\n<ul class=\"wp-block-list\"><li><strong>listen_addr = 127.0.0.1<\/strong> \u2014 Bind to localhost only. Never expose PgBouncer to the public internet.<\/li><li><strong>pool_mode = transaction<\/strong> \u2014 The best mode for web applications. Connections are returned to the pool after each transaction completes.<\/li><li><strong>default_pool_size = 20<\/strong> \u2014 Only 20 server connections are kept open per database, regardless of how many clients connect.<\/li><li><strong>max_client_conn = 500<\/strong> \u2014 The maximum number of client connections PgBouncer will accept. Excess connections queue.<\/li><\/ul>\n\n<h2 class=\"wp-block-heading\">Step 3: Set Up Authentication<\/h2>\n\n<p class=\"wp-block-paragraph\">Create the <code>userlist.txt<\/code> file at <code>\/etc\/pgbouncer\/userlist.txt<\/code> with the database users PgBouncer will authenticate. The password must be stored as an MD5 hash in PostgreSQL format:<\/p>\n\n<pre class=\"wp-block-code\"><code># Generate the MD5 hash in psql\npsql -c \"SELECT concat('md5', md5('yourpassword' || 'yourusername'));\"\n\n# Example output: md5a105d2e4e8c07c6c5e5b5b5b5b5b5b5b\n\n# Add to \/etc\/pgbouncer\/userlist.txt\n\"myappuser\" \"md5a105d2e4e8c07c6c5e5b5b5b5b5b5b5b\"<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Alternatively, copy the <code>rolpassword<\/code> value directly from PostgreSQL&#8217;s <code>pg_authid<\/code> table if you have superuser access.<\/p>\n\n<h2 class=\"wp-block-heading\">Step 4: Start and Enable PgBouncer<\/h2>\n\n<pre class=\"wp-block-code\"><code>sudo systemctl restart pgbouncer\nsudo systemctl enable pgbouncer\nsudo systemctl status pgbouncer<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Check the logs for any errors:<\/p>\n\n<pre class=\"wp-block-code\"><code>sudo journalctl -u pgbouncer --no-pager -n 20<\/code><\/pre>\n\n<h2 class=\"wp-block-heading\">Step 5: Test the Connection<\/h2>\n\n<p class=\"wp-block-paragraph\">Connect to PgBouncer using psql on port 6432:<\/p>\n\n<pre class=\"wp-block-code\"><code>psql -h 127.0.0.1 -p 6432 -U myappuser -d myapp<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">If authentication works, you are connected through PgBouncer. To verify, run the PgBouncer admin commands:<\/p>\n\n<pre class=\"wp-block-code\"><code>psql -h 127.0.0.1 -p 6432 -U myappuser -d pgbouncer -c \"SHOW POOLS;\"\npsql -h 127.0.0.1 -p 6432 -U myappuser -d pgbouncer -c \"SHOW STATS;\"<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">The <code>SHOW POOLS<\/code> output shows the number of active server connections, clients waiting, and the current pool size. If <code>maxwait<\/code> is consistently above 0, your pool is too small for the workload.<\/p>\n\n<h2 class=\"wp-block-heading\">Step 6: Point Your Application at PgBouncer<\/h2>\n\n<p class=\"wp-block-paragraph\">Update your application&#8217;s database connection string to use <code>127.0.0.1:6432<\/code> instead of the default PostgreSQL port 5432. Keep the same database name and credentials.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Important:<\/strong> Keep one direct connection to PostgreSQL on port 5432 for administrative tasks like schema migrations. PgBouncer in transaction mode does not handle prepared statements or session-scoped features like <code>LISTEN\/NOTIFY<\/code> well. For those use cases, either:<\/p>\n\n<ul class=\"wp-block-list\"><li>Use a separate database entry in <code>pgbouncer.ini<\/code> with <code>pool_mode=session<\/code><\/li><li>Or connect directly to PostgreSQL on port 5432<\/li><\/ul>\n\n<h2 class=\"wp-block-heading\">Step 7: Size Your Pool Correctly<\/h2>\n\n<p class=\"wp-block-paragraph\">A good starting point for a 2 vCPU \/ 4 GB VPS:<\/p>\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Setting<\/th><th>Value<\/th><th>Why<\/th><\/tr><\/thead><tbody><tr><td>default_pool_size<\/td><td>10\u201320<\/td><td>Each server connection uses ~5\u201310 MB; 20 is safe for 4 GB<\/td><\/tr><tr><td>max_client_conn<\/td><td>500\u20131000<\/td><td>Clients only queue; cap them to prevent runaway apps<\/td><\/tr><tr><td>reserve_pool_size<\/td><td>5<\/td><td>Reserved for admin queries so the app can&#8217;t starve you out<\/td><\/tr><tr><td>server_idle_timeout<\/td><td>300<\/td><td>Close idle server connections after 5 minutes to free RAM<\/td><\/tr><\/tbody><\/table><\/figure>\n\n<p class=\"wp-block-paragraph\">Monitor pool usage with <code>SHOW POOLS<\/code>. If <code>sv_active<\/code> regularly equals <code>default_pool_size<\/code> and clients are queueing, increase the pool size. If server connections sit idle, reduce it.<\/p>\n\n<h2 class=\"wp-block-heading\">Step 8: Secure the Setup<\/h2>\n\n<ul class=\"wp-block-list\"><li>Bind PgBouncer to <code>127.0.0.1<\/code> only \u2014 never expose port 6432 publicly<\/li><li>Use <code>auth_type = md5<\/code> or <code>scram-sha-256<\/code>, never <code>trust<\/code><\/li><li>Set <code>statement_timeout<\/code> in PostgreSQL so one slow query cannot hog a pooled connection<\/li><li>For remote clients, add TLS with <code>client_tls_sslmode = require<\/code><\/li><\/ul>\n\n<h2 class=\"wp-block-heading\">Troubleshooting Common Issues<\/h2>\n\n<ul class=\"wp-block-list\"><li><strong>Authentication failures:<\/strong> Double-check the MD5 hash in <code>userlist.txt<\/code>. The format is <code>md5<\/code> + <code>md5(password + username)<\/code>.<\/li><li><strong>Prepared statement errors:<\/strong> Set <code>server_prepare_mode = statement<\/code> in the database section of <code>pgbouncer.ini<\/code> for clients that use prepared statements.<\/li><li><strong>High maxwait:<\/strong> Your <code>default_pool_size<\/code> is too small. Increase it gradually and re-check.<\/li><li><strong>PostgreSQL still uses too much RAM:<\/strong> Reduce <code>shared_buffers<\/code> and <code>work_mem<\/code> before growing the pool.<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">PgBouncer is one of the highest-value optimizations for PostgreSQL on a small VPS. It removes the most common memory ceiling \u2014 the per-connection overhead \u2014 and lets a 2\u20134 GB server handle workloads that would otherwise require a much larger instance. For more PostgreSQL and VPS performance guides, <a href=\"https:\/\/virtualserversvps.com\/\">visit the main site<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>PostgreSQL creates one OS process per connection, and each idle connection can consume 5\u201310 MB of RAM. On a small VPS with 2\u20134 GB of memory, an application that opens&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":0,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-941","post","type-post","status-publish","format-standard","hentry","category-performance-optimization"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"PostgreSQL creates one OS process per connection, and each idle connection can consume 5\u201310 MB of RAM. On a small VPS with 2\u20134 GB of memory, an\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers\" \/>\n<meta property=\"og:description\" content=\"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-21T23:27:41+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-26T22:04:22+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/\",\"name\":\"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-08-21T23:27:41+00:00\",\"dateModified\":\"2026-08-26T22:04:22+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"PostgreSQL creates one OS process per connection, and each idle connection can consume 5\u201310 MB of RAM. On a small VPS with 2\u20134 GB of memory, an\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers - Virtual Servers VPS Blog","description":"PostgreSQL creates one OS process per connection, and each idle connection can consume 5\u201310 MB of RAM. On a small VPS with 2\u20134 GB of memory, an","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/","og_locale":"en_US","og_type":"article","og_title":"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers","og_description":"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers","og_url":"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-08-21T23:27:41+00:00","article_modified_time":"2026-08-26T22:04:22+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/","url":"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/","name":"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-08-21T23:27:41+00:00","dateModified":"2026-08-26T22:04:22+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"PostgreSQL creates one OS process per connection, and each idle connection can consume 5\u201310 MB of RAM. On a small VPS with 2\u20134 GB of memory, an","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/pgbouncer-connection-pooling-postgresql-vps\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"PgBouncer on a VPS: Step-by-Step PostgreSQL Connection Pooling for 2\u20134 GB Servers"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/941","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=941"}],"version-history":[{"count":2,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/941\/revisions"}],"predecessor-version":[{"id":976,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/941\/revisions\/976"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=941"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=941"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=941"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}