Odyssey
Overview
Odyssey is a scalable, multithreaded PostgreSQL connection pooler and request router (BSD 3-Clause, from Yandex). This guide covers deploying and using it on Ubuntu 24.04 on Azure with a SecureAnyCloud Azure Marketplace image. The image builds Odyssey from source and bundles PostgreSQL 17, so the appliance works on its own.
Clients connect through Odyssey on port 6432 in transaction pooling mode. Odyssey multiplexes many short-lived client connections onto a small pool of backend PostgreSQL sessions. A connection-heavy application stays responsive, and the database is not exhausted by one backend process per client. Because Odyssey is multithreaded, it scales that work across CPU cores. You can also point Odyssey at your own external PostgreSQL instead of the bundled one.
The bundled PostgreSQL is loopback only. It listens on 127.0.0.1:5432 and is never reachable on the routable address. Odyssey on 6432 is the single public entry point, protected by three layers: the Azure Network Security Group, TLS, and a per-VM scram-sha-256 password.
Security by design: no baked credential. There are two built-in accounts, and neither has a password baked into the image.
appuser is the pooled application role that owns the default database appdb. One per-VM secret authenticates both the client leg (into Odyssey) and the backend leg (Odyssey into PostgreSQL). The postgres superuser is for local administration over the loopback.
Both accounts are passwordless by construction in the captured image, and Odyssey will not start until first boot has written the per-VM secret into its configuration. On first boot, each VM creates a unique password for each account and a unique self-signed TLS server certificate. It writes the secrets to the root-only file /root/odyssey-credentials.txt, fills the pooler configuration and starts Odyssey.
What you get:
- Odyssey 1.5.1 built from source (BSD 3-Clause), running under systemd as
odyssey.service, listening on6432in transaction pooling mode with TLS andscram-sha-256 - PostgreSQL 17 from the official PGDG repository, running as
postgresql.service, loopback only on127.0.0.1:5432 - A default database
appdbowned by the pooled roleappuser - Per-VM passwords for
appuserand thepostgressuperuser, plus a per-VM TLS certificate, created on first boot and written to a root-only credentials file - Odyssey configured so remote clients connect only over TLS with a per-VM password, while local PostgreSQL administration stays passwordless through the unix socket
- A pooling self-test (
/usr/local/sbin/odyssey-selftest.sh) that you can run to prove real multiplexing on your own VM - Unattended security upgrades left enabled, so the appliance keeps receiving patches
Prerequisites
Before you deploy, make sure you have:
- An active Azure subscription, an SSH public key, and a VNet and subnet in the target region
- A subscription to this listing on Azure Marketplace
- A Network Security Group that allows TCP 22 (administration) and TCP 6432 (the Odyssey pooler). In production, restrict
6432to your application subnet. The bundled PostgreSQL on5432is loopback only and never needs an NSG rule. - Recommended virtual machine size:
Standard_B2s(2 vCPU, 4 GB RAM) for development and light workloads. For higher connection counts and throughput, choose a larger size such asStandard_E2s_v5or above, and raise Odyssey'sworkersto match the core count.
Deploy the virtual machine
Create the VM from the image, and open SSH and the pooler port to your own network.
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:odyssey-ubuntu-24-04:default:latest
az vm create \
--resource-group my-rg \
--name odyssey-1 \
--image sac1647283583153:odyssey-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
az vm open-port --resource-group my-rg --name odyssey-1 --port 6432 --priority 900Retrieve your per-VM credentials
On first boot, the VM creates its own passwords and TLS certificate. SSH in and read the root-only credentials file.
sudo cat /root/odyssey-credentials.txtThe file is mode 0600, owned by root, and lists the appuser pooled password (used through Odyssey on 6432) and the postgres superuser password (for local administration on 5432), plus ready-to-paste connection strings. Nothing is baked into the image. Every deployed VM has its own passwords and certificate.
Confirm the services are healthy
Check that both PostgreSQL and Odyssey are active and listening. Also confirm that PostgreSQL is bound only to the loopback address.
sudo systemctl is-active postgresql.service odyssey.service
sudo ss -tln | grep -E ':5432|:6432'You should see both services report active, Odyssey listening on 6432 on all interfaces, and PostgreSQL listening on 5432 only on 127.0.0.1. It is never exposed on the routable address.
Connect through the pooler
On the VM, you can verify the full pooled path in one step. This command reads the per-VM password straight from the credentials file and connects through Odyssey on 6432.
PGPASSWORD="$(sudo sed -n 's/^odyssey.password=//p' /root/odyssey-credentials.txt)" \
psql "host=127.0.0.1 port=6432 dbname=appdb user=appuser sslmode=require" \
-c "SELECT version();" \
-c "SELECT current_user, current_database();"The query runs against PostgreSQL 17 by way of Odyssey and returns as appuser on appdb. The connection uses sslmode=require, so it succeeds only because Odyssey presents its per-VM TLS certificate on 6432. The pooler refuses a plaintext client (sslmode=disable). Odyssey terminates the client TLS and forwards the query to PostgreSQL over the loopback.
From a remote client, connect to the pooler over TLS with the per-VM password from the credentials file. Replace <vm-ip> with your VM's address and <ODYSSEY_PASSWORD> with the value from the file.
Set the password as an environment variable so it does not appear in your shell history or process list.
export PGPASSWORD=<ODYSSEY_PASSWORD> psql "host=<vm-ip> port=6432 dbname=appdb user=appuser sslmode=require" -c "SELECT now();" unset PGPASSWORDProve real connection pooling
The reason to run a pooler is multiplexing: many client connections share a small number of backend database connections. This image ships a self-test that proves it on your own VM.
The test opens more concurrent client connections through Odyssey than the backend pool size (pool_size is 10), each holding a transaction. It then counts the backend PostgreSQL connections that Odyssey actually opened.
sudo /usr/local/sbin/odyssey-selftest.shIt reports that 24 concurrent clients were multiplexed onto no more than 10 backend server connections, reusing a small set of backend process IDs. It also reports that wrong and blank passwords are rejected through the pooler while PostgreSQL stays loopback only.
You can see the same effect live. Open several clients that each hold a transaction, then count the backend connections directly on the box.
# In one shell, open 24 pooled clients that each hold a 15s transaction: PW="$(sudo sed -n 's/^odyssey.password=//p' /root/odyssey-credentials.txt)" for i in $(seq 1 24); do PGPASSWORD="$PW" psql "host=127.0.0.1 port=6432 dbname=appdb user=appuser sslmode=require" \
-qtAX -c 'BEGIN' -c 'SELECT pg_sleep(15)' -c 'COMMIT' >/dev/null 2>&1 & done
# Then count the backend connections Odyssey opened to PostgreSQL:
sudo -u postgres psql -tAc \
"SELECT count(*) FROM pg_stat_activity WHERE datname='appdb' AND usename='appuser';"The backend count stays at or below 10 even with 24 clients connected. That gap is the pooling.
To tune it, raise or lower pool_size in the appdb route of /etc/odyssey/odyssey.conf, then run sudo systemctl restart odyssey.
Inspect the pooler with the Odyssey admin console
Odyssey exposes a built-in admin console as a virtual console database. Connect to it as appuser to read live pooler statistics. The per-VM password is in the credentials file.
PGPASSWORD="$(sudo sed -n 's/^odyssey.password=//p' /root/odyssey-credentials.txt)" \
psql "host=127.0.0.1 port=6432 dbname=console user=appuser sslmode=require" \
-c "SHOW STATS;"SHOW STATS reports per-database transaction, query and wait counters for the pooler. Odyssey also writes periodic statistics to its journal. To follow them, run sudo journalctl -u odyssey.service -f.
Point Odyssey at your own PostgreSQL (optional)
To use Odyssey in front of an existing external PostgreSQL instead of the bundled one, edit /etc/odyssey/odyssey.conf:
- In the
postgres_serverstorage block, sethostandportto your database. - On the
appdbroute, setstorage_user,storage_dbandstorage_passwordto your database's credentials. - Restart the pooler.
sudo systemctl restart odyssey.serviceIf you no longer need the bundled database, you can stop and disable it with sudo systemctl disable --now postgresql.service.
Security posture
No baked credential. Both appuser and the postgres superuser ship without a password. The per-VM passwords are created only on first boot and stored in /root/odyssey-credentials.txt (mode 0600, root only). Odyssey will not start until first boot has filled its configuration. The pooler configuration file is mode 0640 and never world-readable.
PostgreSQL is loopback only. The database listens on 127.0.0.1:5432 and refuses connections on the routable address, so the only way in is through Odyssey.
TLS for every remote connection. Odyssey requires TLS on 6432. Each VM mints its own self-signed certificate on first boot, so no certificate is shared between deployments.
Local access stays passwordless through the unix socket for on-box administration (sudo -u postgres psql).
Network. Expose only TCP 22 and TCP 6432 in your NSG, and restrict 6432 to your application subnet. The NSG is the first layer. TLS and the per-VM password are the second.
To rotate the appuser password later:
- Run
sudo -u postgres psqlandALTER ROLE appuser PASSWORD '<new>';. - Update both the
passwordandstorage_passwordvalues on theappdbroute in/etc/odyssey/odyssey.confto match. - Restart the pooler with
sudo systemctl restart odyssey. - Update the credentials file.
Operations
Manage both services through systemd.
sudo systemctl is-active postgresql.service odyssey.service systemctl show postgresql.service odyssey.service -p Id,ActiveState,SubState,MainPIDBoth services should report active / running. You can inspect the logs at any time with sudo journalctl -u odyssey.service --no-pager | tail -20 or sudo journalctl -u postgresql@17-main.service --no-pager | tail -20.
Odyssey's configuration lives at /etc/odyssey/odyssey.conf. The PostgreSQL data directory lives under /var/lib/postgresql/17/main.
To take a logical backup of a database, use pg_dump, for example sudo -u postgres pg_dump appdb > appdb.sql.
Support
Every SecureAnyCloud image includes 24/7 support. If you have questions about this Odyssey connection pooler image, contact us at sales@secureanycloud.com.
Deploy on Azure
Launch Odyssey PostgreSQL Connection Pooler on Ubuntu 24.04 on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com