Skip to content

Instantly share code, notes, and snippets.

@jirevwe
Created April 16, 2026 12:37
Show Gist options
  • Select an option

  • Save jirevwe/1738c90126405e145be7aacb19947eaa to your computer and use it in GitHub Desktop.

Select an option

Save jirevwe/1738c90126405e145be7aacb19947eaa to your computer and use it in GitHub Desktop.
PGBouncer + Postgres dashboard
#!/bin/bash
# PGBouncer + Postgres dashboard
#
# Usage:
# ./pgbouncer_stats.sh # uses env vars or defaults
# ./pgbouncer_stats.sh --watch # refresh every 5s
# ./pgbouncer_stats.sh --watch 2 # refresh every 2s
#
# Environment variables (all optional):
# PGB_HOST PGBouncer host (default: localhost)
# PGB_PORT PGBouncer port (default: 6432)
# PGB_USER PGBouncer admin user (default: postgres)
# PGB_PASSWORD PGBouncer admin password (default: pg_password)
# PG_HOST Postgres host (default: localhost)
# PG_PORT Postgres port (default: 5432)
# PG_USER Postgres user (default: convoy)
# PG_PASSWORD Postgres password (default: pg_password)
# PG_DATABASE Postgres database (default: convoy)
set -euo pipefail
# Config with defaults
PGB_HOST="${PGB_HOST:-localhost}"
PGB_PORT="${PGB_PORT:-6432}"
PGB_USER="${PGB_USER:-postgres}"
PGB_PASSWORD="${PGB_PASSWORD:-pg_password}"
PG_HOST="${PG_HOST:-localhost}"
PG_PORT="${PG_PORT:-6432}"
PG_USER="${PG_USER:-convoy}"
PG_PASSWORD="${PG_PASSWORD:-pg_password}"
PG_DATABASE="${PG_DATABASE:-convoy}"
# Auto-detect: use local psql if available, otherwise try docker
PGB_DOCKER="${PGB_DOCKER:-local-pgbouncer-1}"
PG_DOCKER="${PG_DOCKER:-local-postgres-1}"
pgb_query() {
if command -v psql &>/dev/null; then
PGPASSWORD="$PGB_PASSWORD" psql -h "$PGB_HOST" -p "$PGB_PORT" -U "$PGB_USER" pgbouncer -t -A -F"|" -c "$1" 2>/dev/null
else
docker exec "$PGB_DOCKER" sh -c "PGPASSWORD=$PGB_PASSWORD psql -h 127.0.0.1 -p 6432 -U $PGB_USER pgbouncer -t -A -F\"|\" -c \"$1\"" 2>/dev/null
fi
}
pg_query() {
if command -v psql &>/dev/null; then
PGPASSWORD="$PG_PASSWORD" psql -h "$PG_HOST" -p "$PG_PORT" -U "$PG_USER" -d "$PG_DATABASE" -t -A -c "$1" 2>/dev/null
else
docker exec "$PG_DOCKER" psql -U "$PG_USER" -d "$PG_DATABASE" -t -A -c "$1" 2>/dev/null
fi
}
fmt() { printf "%'d" "$1" 2>/dev/null || echo "$1"; }
fmt_us() {
local us=$1
if [ "$us" -ge 1000000 ] 2>/dev/null; then
echo "$((us / 1000000)).$((us % 1000000 / 1000))s"
elif [ "$us" -ge 1000 ] 2>/dev/null; then
echo "$((us / 1000))ms"
else
echo "${us}us"
fi
}
run_once() {
# Fetch stats
POOLS=$(pgb_query "SHOW POOLS;" | grep -v "^pgbouncer" | head -1)
STATS=$(pgb_query "SHOW STATS;" | grep -v "^pgbouncer" | head -1)
PG_BACKENDS=$(pg_query "SELECT count(*), count(*) FILTER (WHERE state='active'), count(*) FILTER (WHERE state='idle') FROM pg_stat_activity WHERE backend_type='client backend';")
PG_MAX=$(pg_query "SHOW max_connections;")
if [ -z "$POOLS" ]; then
echo "ERROR: Could not connect to PGBouncer at $PGB_HOST:$PGB_PORT"
echo "Set PGB_HOST, PGB_PORT, PGB_USER, PGB_PASSWORD env vars."
exit 1
fi
if [ -z "$PG_BACKENDS" ]; then
echo "ERROR: Could not connect to Postgres at $PG_HOST:$PG_PORT"
echo "Set PG_HOST, PG_PORT, PG_USER, PG_PASSWORD, PG_DATABASE env vars."
exit 1
fi
# Parse PGBouncer pools
CL_ACTIVE=$(echo "$POOLS" | cut -d'|' -f3)
CL_WAITING=$(echo "$POOLS" | cut -d'|' -f4)
SV_ACTIVE=$(echo "$POOLS" | cut -d'|' -f7)
SV_IDLE=$(echo "$POOLS" | cut -d'|' -f10)
MAXWAIT=$(echo "$POOLS" | cut -d'|' -f14)
POOL_MODE=$(echo "$POOLS" | cut -d'|' -f16 | tr -d ' ')
# Parse PGBouncer stats
TOTAL_XCT=$(echo "$STATS" | cut -d'|' -f3)
TOTAL_QUERY=$(echo "$STATS" | cut -d'|' -f4)
AVG_QUERY_US=$(echo "$STATS" | cut -d'|' -f14)
TOTAL_WAIT_US=$(echo "$STATS" | cut -d'|' -f9)
# Parse Postgres
PG_TOTAL=$(echo "$PG_BACKENDS" | cut -d'|' -f1)
PG_ACTIVE=$(echo "$PG_BACKENDS" | cut -d'|' -f2)
PG_IDLE=$(echo "$PG_BACKENDS" | cut -d'|' -f3)
# Multiplexing ratio
SV_TOTAL=$((SV_ACTIVE + SV_IDLE))
if [ "$SV_TOTAL" -gt 0 ] 2>/dev/null; then
RATIO="$CL_ACTIVE clients -> $SV_TOTAL PG backends ($PG_MAX max)"
else
RATIO="$CL_ACTIVE clients -> $PG_TOTAL PG backends ($PG_MAX max)"
fi
echo ""
echo "=== PGBouncer + Postgres Dashboard === ($(date '+%H:%M:%S'))"
echo ""
printf "%-38s %s\n" "Pool mode:" "$POOL_MODE"
printf "%-38s %s\n" "Client connections (active):" "$CL_ACTIVE"
printf "%-38s %s\n" "Client connections (waiting):" "$CL_WAITING"
printf "%-38s %s\n" "Max wait (seconds):" "$MAXWAIT"
printf "%-38s %s\n" "Server connections (active):" "$SV_ACTIVE"
printf "%-38s %s\n" "Server connections (idle):" "$SV_IDLE"
echo ""
printf "%-38s %s\n" "Total transactions:" "$(fmt $TOTAL_XCT)"
printf "%-38s %s\n" "Total queries:" "$(fmt $TOTAL_QUERY)"
printf "%-38s %s\n" "Avg query time:" "$(fmt_us $AVG_QUERY_US)"
printf "%-38s %s\n" "Total wait time:" "$(fmt_us $TOTAL_WAIT_US)"
echo ""
printf "%-38s %s\n" "Postgres backend connections:" "$PG_TOTAL"
printf "%-38s %s\n" "Postgres active backends:" "$PG_ACTIVE"
printf "%-38s %s\n" "Postgres idle backends:" "$PG_IDLE"
printf "%-38s %s\n" "Postgres max_connections:" "$PG_MAX"
echo ""
printf "%-38s %s\n" "Connection multiplexing:" "$RATIO"
echo ""
}
# Handle --watch mode
if [ "${1:-}" = "--watch" ]; then
INTERVAL="${2:-5}"
while true; do
clear
run_once
sleep "$INTERVAL"
done
else
run_once
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment