Supabase Connection Playbook¶
How to connect to Supabase Postgres from any container, app, or client. Covers session pool, transaction pool, and direct internal access.
Quick reference¶
# Session pool (recommended for apps with ORM)
postgresql://postgres.homelab:${DB_PASSWORD}@supabase.tail1137c.ts.net:5432/postgres
# Transaction pool (serverless / high concurrency)
postgresql://postgres.homelab:${DB_PASSWORD}@supabase.tail1137c.ts.net:6543/postgres
# HTTPS API (Studio, REST, Auth, Storage — for frontend / SDK)
https://supabase.tail1137c.ts.net
Credentials: SUPABASE_POSTGRES_PASSWORD Komodo Variable.
Connection endpoints¶
Via Tailscale (recommended — any Tailscale device)¶
| Port | URL / Host:Port | Protocol | Use case |
|---|---|---|---|
| 443 | https://supabase.tail1137c.ts.net |
HTTPS | Studio UI, REST API, Auth, Storage, Realtime |
| 5432 | supabase.tail1137c.ts.net:5432 |
PostgreSQL | Session pool — long-lived connections, prepared statements |
| 6543 | supabase.tail1137c.ts.net:6543 |
PostgreSQL | Transaction pool — high concurrency, short queries |
Via Cloudflare Tunnel (public internet)¶
| Endpoint | URL | Use case |
|---|---|---|
| HTTPS API | https://supabase.minhluc.info |
Same as Tailscale, but via CF edge (slower, CF Access SSO for Studio) |
Via Tailscale IP (fallback if DNS not resolving)¶
| Port | Host:Port | Use case |
|---|---|---|
| 5432 | supabase.tail1137c.ts.net:5432 |
Session pool |
| 6543 | supabase.tail1137c.ts.net:6543 |
Transaction pool |
Internal Docker network (supabase-internal)¶
| Endpoint | Host:Port | Use case |
|---|---|---|
| Direct DB | db:5432 |
Supabase internal services only |
| Pooler | supabase-pooler:5432 |
Internal services with pooling |
Connection string format¶
{TENANT_ID}=homelab(Komodo VariablePOOLER_TENANT_ID){PASSWORD}= value ofSUPABASE_POSTGRES_PASSWORDKomodo Variable{HOST}=supabase.tail1137c.ts.net(Tailscale) orsupabase.tail1137c.ts.net(LAN){PORT}=5432(session) or6543(transaction){DATABASE}=postgres(default)
Session pool (port 5432)¶
- ✅ Prepared statements work
- ✅ Session state (SET, temp tables, LISTEN/NOTIFY) preserved
- Pool size: 20 connections (configurable via
POOLER_DEFAULT_POOL_SIZE)
Transaction pool (port 6543)¶
- ❌ Prepared statements NOT supported
- ❌ Session state lost between queries
- Max client connections: 100
Schema access¶
To use a specific schema (e.g., njav), append ?options=-c search_path=SCHEMA:
postgresql://postgres.homelab:${DB_PASSWORD}@supabase.tail1137c.ts.net:5432/postgres?options=-c%20search_path%3Dnjav
Or in SQL after connecting:
Or create a dedicated role with default schema:
CREATE ROLE njav_user LOGIN PASSWORD 'Set via Komodo Variable NJAV_DB_PASSWORD';
GRANT USAGE ON SCHEMA njav TO njav_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA njav TO njav_user;
ALTER ROLE njav_user SET search_path = njav;
Connecting from a new stack¶
Via Tailscale domain (recommended — stable, never changes)¶
Container stays on its own network, connects via unraid LAN IP:
# stacks/{category}/{service}/compose.yaml
services:
myapp:
image: myapp:latest
environment:
# Session pool via LAN
DATABASE_URL: postgresql://postgres.homelab:${DB_PASSWORD}@supabase.tail1137c.ts.net:5432/postgres
extra_hosts:
- host.docker.internal:host-gateway
# komodo/stacks/{category}/{service}.toml
environment = """
DB_PASSWORD=[[SUPABASE_POSTGRES_PASSWORD]]
"""
Via Tailscale IP (for containers without MagicDNS)¶
environment:
DATABASE_URL: postgresql://postgres.homelab:${DB_PASSWORD}@supabase.tail1137c.ts.net:5432/postgres
(Requires Tailscale running on the container host.)
Option C: Same Docker network (supabase-internal — direct DB, no pooler)¶
⚠️ This bypasses the pooler — use ONLY for admin tasks (migrations, schema changes).
Code examples¶
Python (psycopg / SQLAlchemy)¶
import psycopg
conn = psycopg.connect(
"postgresql://postgres.homelab:${DB_PASSWORD}@supabase.tail1137c.ts.net:5432/postgres"
)
from sqlalchemy import create_engine
engine = create_engine(
"postgresql://postgres.homelab:${DB_PASSWORD}@supabase.tail1137c.ts.net:5432/postgres",
pool_size=5,
)
Node.js (pg / Prisma)¶
// pg
const { Pool } = require('pg')
const pool = new Pool({
connectionString: 'postgresql://postgres.homelab:${DB_PASSWORD}@supabase.tail1137c.ts.net:5432/postgres'
})
// Prisma schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL") // postgresql://postgres.homelab:${DB_PASSWORD}@...
}
Supabase JS SDK (REST API, not Postgres protocol)¶
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
"https://supabase.tail1137c.ts.net",
"YOUR_ANON_KEY", // SUPABASE_ANON_KEY
);
// Query njav schema
const { data } = await supabase.schema("njav").from("table").select("*");
psql (CLI)¶
# Session pool
PGPASSWORD='(from SUPABASE_POSTGRES_PASSWORD Komodo Variable)' psql "host=supabase.tail1137c.ts.net port=5432 user=postgres.homelab dbname=postgres"
# Transaction pool
PGPASSWORD='(from SUPABASE_POSTGRES_PASSWORD Komodo Variable)' psql "host=supabase.tail1137c.ts.net port=6543 user=postgres.homelab dbname=postgres"
# With njav schema
PGPASSWORD='(from SUPABASE_POSTGRES_PASSWORD Komodo Variable)' psql "host=supabase.tail1137c.ts.net port=5432 user=postgres.homelab dbname=postgres" \
-c "SET search_path TO njav; SELECT * FROM your_table LIMIT 5;"
When to use which¶
graph TD
ROOT["Need DB access?"]
ROOT --> ORM["App with ORM?
(Prisma, SQLAlchemy, Django, Rails)"]
ORM -->|Yes| SP["Session pool :5432 ✅"]
ROOT --> SERVERLESS["Serverless / edge / short-lived?"]
SERVERLESS -->|Yes| TP["Transaction pool :6543 ✅"]
ROOT --> ADMIN["Admin task?
(migrations, schema)"]
ADMIN -->|Yes| DD["Direct DB internal db:5432 ✅"]
ROOT --> FE["Frontend (browser)?"]
FE -->|Yes| SDK["Supabase JS SDK via HTTPS ✅"]
ROOT --> CLI["Quick CLI query?"]
CLI -->|Yes| PS["psql session pool :5432 ✅"]
For other stacks: how to connect (step-by-step)¶
-
Get the password: Komodo Variable
SUPABASE_POSTGRES_PASSWORD -
Add to your stack's TOML:
environment = """
SUPABASE_DB_URL=postgresql://postgres.homelab:[[SUPABASE_POSTGRES_PASSWORD]]@supabase.tail1137c.ts.net:5432/postgres
"""
- Reference in compose.yaml:
- Create your schema/table:
PGPASSWORD='(from SUPABASE_POSTGRES_PASSWORD Komodo Variable)' psql "host=supabase.tail1137c.ts.net port=5432 user=postgres.homelab dbname=postgres" \
-c "CREATE SCHEMA IF NOT EXISTS myapp;"
- Grant permissions (recommended: dedicated role per app):
CREATE ROLE myapp_user LOGIN PASSWORD 'Set via Komodo Variable MYAPP_DB_PASSWORD';
GRANT USAGE ON SCHEMA myapp TO myapp_user;
GRANT ALL ON ALL TABLES IN SCHEMA myapp TO myapp_user;
- Update connection string to use the new role:
Secrets reference¶
| Komodo Variable | Used for |
|---|---|
SUPABASE_POSTGRES_PASSWORD |
DB password (used by pooler + direct — but postgres role is NOT superuser, see below) |
SUPABASE_ANON_KEY |
Public API key (Supabase JS SDK) |
SUPABASE_SERVICE_ROLE_KEY |
Admin API key (bypasses RLS) |
SUPABASE_S3_PROTOCOL_ACCESS_KEY_ID |
S3 protocol access (Storage) |
SUPABASE_S3_PROTOCOL_ACCESS_KEY_SECRET |
S3 protocol secret (Storage) |
Admin DB Access (writing to Supabase DB)¶
The postgres role connected via pooler is NOT a superuser (rolsuper=f).
It lacks UPDATE/DELETE privileges on tables owned by other schemas.
supabase_admin is the real superuser (rolsuper=t). Use it for one-off admin
SQL (schema changes, system_setting updates, role management):
# Pattern: pipe SQL via SSH to supabase_admin inside the DB container
echo "UPDATE memos.system_setting SET value = '...' WHERE name = 'STORAGE';" \
| ssh root@100.68.251.84 "docker exec -i supabase-db psql -U supabase_admin -d postgres"
Do NOT use:
psql -U postgres.homelab ...(pooler) for writes —permission denied for tablekm-exec supabase-db psql -U postgres -c "SQL WITH SPACES"— km-exec splits args at spaces, breaking the SQL. Use the SSH+pipe pattern above.
Troubleshooting¶
Connection refused on Tailscale ports¶
Sidecar may have lost serve config after deploy. Re-apply:
ssh root@100.68.251.84 'docker exec tailscale-supabase tailscale serve reset'
ssh root@100.68.251.84 'docker exec tailscale-supabase tailscale serve --bg --https 443 http://supabase-kong:8000'
ssh root@100.68.251.84 'docker exec tailscale-supabase tailscale serve --bg --tcp 5432 tcp://supabase-pooler:5432'
ssh root@100.68.251.84 'docker exec tailscale-supabase tailscale serve --bg --tcp 6543 tcp://supabase-pooler:6543'
Tenant not found error¶
Username must be postgres.homelab (format: postgres.{POOLER_TENANT_ID}).
no such host resolving container names¶
You're on a different Docker network. Use LAN IP (supabase.tail1137c.ts.net:5432) or Tailscale URL instead.