Supavisor
Overview
Supavisor is Supabase's cloud-native, scalable Postgres connection pooler. It absorbs very large numbers of client connections and multiplexes them onto a small, stateful pool of native PostgreSQL connections. It is in the same class as PgBouncer, with transaction and session pooling modes and a multi-tenant model.
This SecureAnyCloud image runs Supavisor on Ubuntu 24.04 on Azure. It also installs a local PostgreSQL 16 instance from the official PostgreSQL PGDG repository and wires up one pre-configured demo tenant in front of it. On first boot, Supavisor creates its metadata schema, starts pooling and seeds the demo tenant. You can run a pooled query immediately, then point Supavisor at your own PostgreSQL databases.
What you get:
- Supavisor 2.9.10 built from source as a self-contained Erlang/OTP release at
/opt/supavisor, running under systemd - A bundled local PostgreSQL 16 instance from the PGDG repository, serving both Supavisor's metadata database and the demo tenant, listening on the loopback interface only
- A pre-configured demo tenant, so a pooled query works on first boot, with transaction mode on port
6543and session mode on port5452 - The Supavisor management and metrics API on
127.0.0.1:4000(tenant CRUD plus a Prometheus/metricsendpoint), bound to loopback and JWT-protected - Per-VM secrets generated on first boot, stored in an environment file readable only by root and the service user and summarised in a root-only file, never baked into the image
- Unattended security upgrades left enabled, so the appliance keeps receiving patches
How it works. Clients connect to Supavisor with a username of the form <db_user>.<tenant>, for example demo_user.demo for the demo tenant. Supavisor looks the tenant up in its metadata database. It opens or reuses a pool of native connections to that tenant's upstream PostgreSQL and proxies your session through it.
The port selects the pool mode. Port 6543 is transaction mode (the Supabase convention and the primary service surface). Port 5452 is session mode. Both use the same credential.
Security by design: only the pooler is public. The bundled PostgreSQL listens on 127.0.0.1 only. Supavisor's management and metrics API on port 4000 is also bound to loopback only. In your Network Security Group, expose only TCP 22 for administration and TCP 6543 for the pooler (plus 5452 if you use session mode). The per-tenant credential gates all access, and PostgreSQL is reachable only through the pooler.
Security by design: no baked credential. Nothing is baked into the image. On first boot, every secret is generated uniquely for the VM:
- The PostgreSQL superuser password, the
supavisormetadata role password and thedemo_usertenant password - Supavisor's own
SECRET_KEY_BASEand its 32-byteVAULT_ENC_KEY, which encrypts stored tenant passwords - The API and metrics JWT secrets and the Erlang release cookie
The live values are written to /etc/supavisor/supavisor.env (mode 0640, readable only by root and the service user) and summarised in the root-only file /root/supavisor-credentials.txt. The metadata and demo databases are reset when the image is built, so every deployed VM starts from a clean state with fresh secrets.
Prerequisites
- An active Azure subscription, an SSH public key, and a VNet with a subnet in the target region
- A subscription to the Supavisor listing on the Azure Marketplace
- A Network Security Group that allows TCP 22 (admin) and TCP 6543 (the transaction pooler) from your client network, plus TCP 5452 if you use session mode
This appliance exposes no PostgreSQL port.
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) for the demo and light pooling. For production pooling in front of a busy database, use Standard_D2s_v5 or larger. This gives the Erlang VM headroom for many thousands of client connections.
Deploy from the Azure Portal
In the Azure Marketplace, search for Supavisor, select the SecureAnyCloud publisher, and click Create.
Configure the Network Security Group to allow TCP 22 for administration and TCP 6543 for the pooler from your client network. Add TCP 5452 as well if you plan to use session mode.
No inbound PostgreSQL port is required. The bundled PostgreSQL is loopback-only and is reached only through the pooler.
Deploy from the Azure CLI
Create the resource group and VM, then open ports 22 and 6543.
RG="pooler-prod"; LOCATION="eastus"; VM_NAME="supavisor1" IMAGE_URN="sac1647283583153:supavisor-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:supavisor-ubuntu-24-04:default:latest
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$IMAGE_URN" \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values "$SSH_KEY" \
--public-ip-sku Standard
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 6543 --priority 1002Retrieve the connection details
SSH in as azureuser and read the root-only credentials file. It contains the demo tenant's per-VM password and a ready-to-use pooler connection string.
sudo cat /root/supavisor-credentials.txtConfirm the pooler is healthy
Check that both services are active and see which ports are public. Only the pooler ports listen on all interfaces. The PostgreSQL port and the management API stay on loopback.
sudo systemctl is-active supavisor postgresql
sudo ss -tlnp | grep -E ':(6543|5452|4000|5432)' | awk '{print $4}' | sort -uRun a query through the pooler
Connect to the demo tenant through the transaction pooler on port 6543. This example reads the demo password from the credentials file. From your own machine, use the connection string printed in Step 3.
The query shows that the client is proxied to the upstream PostgreSQL, and it returns real rows.
PW=$(sudo sed -n 's/^pooler.demo.password=//p' /root/supavisor-credentials.txt) PGPASSWORD="$PW" psql "host=127.0.0.1 port=6543 dbname=demo user=demo_user.demo sslmode=disable" \
-c "SELECT current_user, inet_server_port() AS upstream_port, substring(version() from 'PostgreSQL [0-9.]+') AS server;" PGPASSWORD="$PW" psql "host=127.0.0.1 port=6543 dbname=demo user=demo_user.demo sslmode=disable" \
-c "SELECT id, message, created_at FROM greeting;"Inspect the tenant configuration
Supavisor stores its tenants and their users in the _supavisor schema of the metadata database. This is the multi-tenant model: each tenant maps to an upstream PostgreSQL, and each tenant user has a pool size and a pool mode.
sudo -u postgres psql -d supavisor \
-c "SELECT external_id AS tenant, db_host, db_port, db_database, require_user FROM _supavisor.tenants;"
sudo -u postgres psql -d supavisor \
-c "SELECT tenant_external_id AS tenant, db_user, mode_type, pool_size FROM _supavisor.users;"See connection pooling in action
Open several client connections through the pooler in quick succession, then look at the upstream PostgreSQL. Transaction pooling multiplexes many clients onto a small pool. The client connections are therefore served by only a handful of native backend connections, all labelled Supavisor.
PW=$(sudo sed -n 's/^pooler.demo.password=//p' /root/supavisor-credentials.txt) for i in $(seq 1 8); do PGPASSWORD="$PW" psql "host=127.0.0.1 port=6543 dbname=demo user=demo_user.demo sslmode=disable" -tAc 'select 1;' >/dev/null; done
sudo -u postgres psql -d demo \
-c "SELECT usename, application_name, state, count(*) AS upstream_backends FROM pg_stat_activity WHERE usename='demo_user' GROUP BY 1,2,3;"Connect your own application
From your application or workstation, connect through the pooler with the connection string from Step 3. Use the transaction pooler on port 6543 for serverless and high-concurrency workloads. Use the session pooler on port 5452 when you need a full PostgreSQL session. The same credential works on both, and the port selects the mode.
# transaction mode (recommended for serverless / high concurrency)
psql "postgres://demo_user.demo:<password>@<public-ip>:6543/demo"
# session mode (a full PostgreSQL session; open TCP 5452 in your NSG first)
psql "postgres://demo_user.demo:<password>@<public-ip>:5452/demo"Point Supavisor at your own database
To pool your own PostgreSQL, add a tenant through the loopback management API. Sign a JWT with the API_JWT_SECRET from /etc/supavisor/supavisor.env. Then create a tenant that references your upstream database and one or more users.
The example below is illustrative. Replace the bearer token with your signed JWT.
curl -sX PUT http://127.0.0.1:4000/api/tenants/myapp \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{"tenant":{"db_host":"your-db.example.com","db_port":5432,"db_database":"appdb","require_user":true,"users":[{"db_user":"app","db_password":"<db-password>","pool_size":15,"mode_type":"transaction"}]}}'Clients then connect as app.myapp on port 6543. See the Supavisor documentation for the full tenant and metrics API.
Security notes
Only the pooler is public. PostgreSQL (5432) and the Supavisor management and metrics API (4000) are bound to 127.0.0.1. Expose only TCP 22 and TCP 6543 (and 5452 for session mode) in your Network Security Group.
No baked credential. Every database password and every Supavisor secret is generated on first boot. They are stored only in /etc/supavisor/supavisor.env (root and service user) and /root/supavisor-credentials.txt (root only). Stored tenant passwords are encrypted with a per-VM key.
Metrics. A Prometheus /metrics endpoint is served on 127.0.0.1:4000, protected by a per-VM metrics JWT secret. Scrape it locally or through a tunnel. Do not expose port 4000.
Keep it patched. Unattended security upgrades remain enabled, so the base OS keeps receiving updates.
Support
This image is maintained by SecureAnyCloud with 24/7 support. For deployment help or issues, contact support through the SecureAnyCloud listing on Azure Marketplace.
Deploy on Azure
Launch Supavisor Postgres Connection Pooler on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com