Set up efficient connection pooling for CockroachDB applications using PgBouncer as a connection pool manager and HAProxy for load balancing across multiple database nodes. This configuration reduces database connection overhead and improves application performance.
Prerequisites
- Root or sudo access
- Running CockroachDB cluster
- Basic understanding of SQL databases
- Network connectivity between nodes
What this solves
CockroachDB applications can overwhelm database nodes with too many concurrent connections, leading to performance degradation and connection errors. This tutorial configures PgBouncer to pool and reuse database connections efficiently, while HAProxy distributes traffic across multiple CockroachDB nodes for high availability and load distribution.
Step-by-step configuration
Update system packages
Start by updating your package manager to ensure you have the latest versions of all packages.
sudo apt update && sudo apt upgrade -y
Install PgBouncer and HAProxy
Install both PgBouncer for connection pooling and HAProxy for load balancing CockroachDB nodes.
sudo apt install -y pgbouncer haproxy
Create PgBouncer user database file
Create a user authentication file for PgBouncer to authenticate with CockroachDB. Replace the password with your actual CockroachDB user password.
sudo mkdir -p /etc/pgbouncer
sudo touch /etc/pgbouncer/userlist.txt
sudo chown pgbouncer:pgbouncer /etc/pgbouncer/userlist.txt
sudo chmod 600 /etc/pgbouncer/userlist.txt
Add your CockroachDB user credentials to the userlist file:
"myapp" "md5a1b2c3d4e5f6789abc123def456789ab"
echo -n 'passwordusername' | md5sum and prefix with "md5".Configure HAProxy for CockroachDB load balancing
Configure HAProxy to distribute connections across multiple CockroachDB nodes with health checks. This configuration provides load balancing and automatic failover.
global
log stdout local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
mode tcp
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
log global
option tcplog
option dontlognull
frontend cockroachdb_frontend
bind *:26257
default_backend cockroachdb_backend
backend cockroachdb_backend
balance roundrobin
option tcp-check
tcp-check connect
server crdb1 203.0.113.10:26257 check
server crdb2 203.0.113.11:26257 check
server crdb3 203.0.113.12:26257 check
frontend stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
Configure PgBouncer for connection pooling
Configure PgBouncer to pool connections efficiently. This configuration uses transaction pooling for optimal performance with CockroachDB.
[databases]
myappdb = host=127.0.0.1 port=26257 dbname=myappdb user=myapp
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
logfile = /var/log/postgresql/pgbouncer.log
pidfile = /var/run/postgresql/pgbouncer.pid
admin_users = pgbouncer
stats_users = pgbouncer
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
max_db_connections = 100
reserve_pool_size = 10
reserve_pool_timeout = 3
max_prepared_statements = 0
server_reset_query = DISCARD ALL
server_check_query = SELECT 1
server_lifetime = 3600
server_idle_timeout = 600
log_connections = 1
log_disconnections = 1
log_pooler_errors = 1
Create PgBouncer systemd service
Create a proper systemd service file for PgBouncer to ensure it starts automatically and runs with correct permissions.
[Unit]
Description=PgBouncer connection pooler
After=network.target
[Service]
Type=notify
User=pgbouncer
Group=pgbouncer
ExecStart=/usr/sbin/pgbouncer /etc/pgbouncer/pgbouncer.ini
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
TimeoutSec=120
Restart=on-failure
[Install]
WantedBy=multi-user.target
Set proper file permissions
Configure correct ownership and permissions for PgBouncer files to ensure security and proper operation.
sudo chown -R pgbouncer:pgbouncer /etc/pgbouncer
sudo chmod 644 /etc/pgbouncer/pgbouncer.ini
sudo chmod 600 /etc/pgbouncer/userlist.txt
sudo mkdir -p /var/log/postgresql
sudo chown pgbouncer:pgbouncer /var/log/postgresql
Configure firewall rules
Open the necessary ports for HAProxy and PgBouncer while maintaining security. HAProxy uses port 26257 for CockroachDB connections and 8404 for stats.
sudo ufw allow 26257/tcp comment 'CockroachDB via HAProxy'
sudo ufw allow 6432/tcp comment 'PgBouncer'
sudo ufw allow 8404/tcp comment 'HAProxy Stats'
sudo ufw reload
Enable and start services
Start both HAProxy and PgBouncer services and enable them to start automatically on boot.
sudo systemctl daemon-reload
sudo systemctl enable --now haproxy
sudo systemctl enable --now pgbouncer
sudo systemctl status haproxy
sudo systemctl status pgbouncer
Configure application connection string
Update your application to connect through PgBouncer instead of directly to CockroachDB. This provides connection pooling benefits.
postgresql://myapp:password@localhost:6432/myappdb?sslmode=disable
Optimize connection pool settings
Tune PgBouncer pool sizes
Adjust pool sizes based on your application's connection patterns and server resources. Start with conservative values and monitor performance.
# Connection pool tuning
default_pool_size = 25 # Connections per user/database
max_db_connections = 100 # Max connections to CockroachDB
reserve_pool_size = 10 # Emergency connections
max_client_conn = 1000 # Max client connections
server_lifetime = 3600 # Connection lifetime (1 hour)
server_idle_timeout = 600 # Idle timeout (10 minutes)
Configure HAProxy connection limits
Set appropriate connection limits and timeouts in HAProxy to prevent overwhelming CockroachDB nodes.
# Add to backend section
backend cockroachdb_backend
balance roundrobin
option tcp-check
tcp-check connect
maxconn 500
server crdb1 203.0.113.10:26257 check maxconn 150
server crdb2 203.0.113.11:26257 check maxconn 150
server crdb3 203.0.113.12:26257 check maxconn 150
Monitor connection pool performance
Monitor PgBouncer statistics
Connect to PgBouncer's admin console to monitor pool usage and performance metrics in real-time.
psql -h 127.0.0.1 -p 6432 -U pgbouncer pgbouncer
Inside PgBouncer admin console:
SHOW STATS;
SHOW POOLS;
SHOW CLIENTS;
SHOW SERVERS;
Monitor HAProxy statistics
Access HAProxy statistics dashboard to monitor backend server health and connection distribution.
# View HAProxy stats in browser
http://your-server:8404/stats
Or check via command line
echo "show stat" | socat stdio /run/haproxy/admin.sock
Verify your setup
# Check service status
sudo systemctl status haproxy pgbouncer
Test connection through PgBouncer
psql -h 127.0.0.1 -p 6432 -U myapp -d myappdb -c "SELECT version();"
Check PgBouncer pool statistics
psql -h 127.0.0.1 -p 6432 -U pgbouncer pgbouncer -c "SHOW STATS;"
Verify HAProxy backend health
curl -s http://localhost:8404/stats | grep cockroachdb_backend
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| PgBouncer won't start | Incorrect file permissions | sudo chown pgbouncer:pgbouncer /etc/pgbouncer/* |
| Authentication failed | Wrong MD5 hash in userlist | Regenerate hash: echo -n 'passwordusername' | md5sum |
| HAProxy backend down | CockroachDB node unreachable | Check CockroachDB service and network connectivity |
| Connection pool exhausted | Pool size too small | Increase default_pool_size and max_db_connections |
| High connection latency | Inefficient pool mode | Use pool_mode = transaction for better throughput |
| SSL connection errors | SSL mismatch between layers | Configure SSL consistently across PgBouncer and HAProxy |
Next steps
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Global variables
SCRIPT_NAME=$(basename "$0")
CRDB_NODES=""
DB_NAME="myappdb"
DB_USER="myapp"
DB_PASSWORD=""
# Usage message
usage() {
echo "Usage: $SCRIPT_NAME --nodes=IP1:PORT,IP2:PORT,IP3:PORT --dbname=NAME --user=USER --password=PASS"
echo "Example: $SCRIPT_NAME --nodes=10.0.1.10:26257,10.0.1.11:26257,10.0.1.12:26257 --dbname=myapp --user=appuser --password=secret123"
exit 1
}
# Parse arguments
for arg in "$@"; do
case $arg in
--nodes=*) CRDB_NODES="${arg#*=}" ;;
--dbname=*) DB_NAME="${arg#*=}" ;;
--user=*) DB_USER="${arg#*=}" ;;
--password=*) DB_PASSWORD="${arg#*=}" ;;
--help|-h) usage ;;
*) echo -e "${RED}Unknown argument: $arg${NC}"; usage ;;
esac
done
# Validate required arguments
if [[ -z "$CRDB_NODES" || -z "$DB_PASSWORD" ]]; then
echo -e "${RED}Error: --nodes and --password are required${NC}"
usage
fi
# Cleanup function
cleanup() {
echo -e "${YELLOW}Script failed. Rolling back changes...${NC}"
systemctl stop pgbouncer haproxy 2>/dev/null || true
systemctl disable pgbouncer haproxy 2>/dev/null || true
}
trap cleanup ERR
# Check prerequisites
echo -e "${GREEN}[1/10] Checking prerequisites...${NC}"
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
;;
*)
echo -e "${RED}Unsupported distro: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}/etc/os-release not found${NC}"
exit 1
fi
# Update system packages
echo -e "${GREEN}[2/10] Updating system packages...${NC}"
$PKG_UPDATE
# Install PgBouncer and HAProxy
echo -e "${GREEN}[3/10] Installing PgBouncer and HAProxy...${NC}"
$PKG_INSTALL pgbouncer haproxy
# Generate MD5 hash for password
echo -e "${GREEN}[4/10] Generating password hash...${NC}"
PASSWORD_HASH="md5$(echo -n "${DB_PASSWORD}${DB_USER}" | md5sum | cut -d' ' -f1)"
# Create PgBouncer user database file
echo -e "${GREEN}[5/10] Creating PgBouncer user database...${NC}"
mkdir -p /etc/pgbouncer
cat > /etc/pgbouncer/userlist.txt << EOF
"$DB_USER" "$PASSWORD_HASH"
EOF
chown pgbouncer:pgbouncer /etc/pgbouncer/userlist.txt
chmod 600 /etc/pgbouncer/userlist.txt
# Configure HAProxy
echo -e "${GREEN}[6/10] Configuring HAProxy...${NC}"
cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.backup
cat > /etc/haproxy/haproxy.cfg << 'EOF'
global
log stdout local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
mode tcp
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
log global
option tcplog
option dontlognull
frontend cockroachdb_frontend
bind *:26257
default_backend cockroachdb_backend
backend cockroachdb_backend
balance roundrobin
option tcp-check
tcp-check connect
EOF
# Parse nodes and add to HAProxy config
IFS=',' read -ra NODES <<< "$CRDB_NODES"
counter=1
for node in "${NODES[@]}"; do
echo " server crdb$counter $node check" >> /etc/haproxy/haproxy.cfg
((counter++))
done
cat >> /etc/haproxy/haproxy.cfg << 'EOF'
frontend stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
EOF
# Configure PgBouncer
echo -e "${GREEN}[7/10] Configuring PgBouncer...${NC}"
cat > /etc/pgbouncer/pgbouncer.ini << EOF
[databases]
$DB_NAME = host=127.0.0.1 port=26257 dbname=$DB_NAME user=$DB_USER
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
logfile = /var/log/postgresql/pgbouncer.log
pidfile = /var/run/postgresql/pgbouncer.pid
admin_users = pgbouncer
stats_users = pgbouncer
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
max_db_connections = 100
reserve_pool_size = 10
reserve_pool_timeout = 3
max_prepared_statements = 0
server_reset_query = DISCARD ALL
server_check_query = SELECT 1
server_lifetime = 3600
server_idle_timeout = 600
log_connections = 1
log_disconnections = 1
log_pooler_errors = 1
EOF
# Set proper permissions
echo -e "${GREEN}[8/10] Setting file permissions...${NC}"
chown -R pgbouncer:pgbouncer /etc/pgbouncer
chmod 644 /etc/pgbouncer/pgbouncer.ini
chmod 600 /etc/pgbouncer/userlist.txt
mkdir -p /var/log/postgresql
chown postgres:postgres /var/log/postgresql
mkdir -p /var/run/postgresql
chown postgres:postgres /var/run/postgresql
# Configure systemd services
echo -e "${GREEN}[9/10] Configuring services...${NC}"
systemctl enable haproxy
systemctl enable pgbouncer
systemctl restart haproxy
systemctl restart pgbouncer
# Configure firewall if available
if command -v ufw >/dev/null 2>&1; then
ufw allow 6432/tcp comment "PgBouncer"
ufw allow 8404/tcp comment "HAProxy stats"
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-port=6432/tcp
firewall-cmd --permanent --add-port=8404/tcp
firewall-cmd --reload
fi
# Verification
echo -e "${GREEN}[10/10] Verifying installation...${NC}"
sleep 3
if systemctl is-active --quiet haproxy; then
echo -e "${GREEN}✓ HAProxy is running${NC}"
else
echo -e "${RED}✗ HAProxy failed to start${NC}"
fi
if systemctl is-active --quiet pgbouncer; then
echo -e "${GREEN}✓ PgBouncer is running${NC}"
else
echo -e "${RED}✗ PgBouncer failed to start${NC}"
fi
echo -e "${GREEN}Installation complete!${NC}"
echo -e "${YELLOW}Connect to PgBouncer on: localhost:6432${NC}"
echo -e "${YELLOW}HAProxy stats available at: http://localhost:8404/stats${NC}"
echo -e "${YELLOW}Connection string: postgresql://$DB_USER:$DB_PASSWORD@localhost:6432/$DB_NAME${NC}"
Review the script before running. Execute with: bash install.sh