Pinned at: develop @ 31d6fb97 · macOS arm64 (Darwin 25.5), Docker Desktop (server 29.5.3), Node 24.18.0, realtime image v2.112.3 (DEFAULT_VERSIONS), consuming @supabase/stack via createStack().
Enabling realtime in docker mode (createStack({ mode: "docker", realtime: { port } })) never reaches ready, and even a healthy realtime container can't be consumed by supabase-js through the stack. Three findings, plus one observation:
1. The realtime container can't reach postgres — start() hangs indefinitely.
The realtime ServiceDef's env (packages/stack/src/services/realtime.ts) doesn't set DB_IP_VERSION. On Docker Desktop for Mac, every connection attempt fails with:
Postgrex.Protocol failed to connect: ** (DBConnection.ConnectionError)
tcp connect (host.docker.internal:53976): network is unreachable - :enetunreach
— while getent ahosts host.docker.internal inside the same image resolves IPv4-only (192.168.65.254), and GoTrue/PostgREST connect fine over the identical wiring. The migration runner gives up after ~15s, the container exits, unless-stopped restart-loops it forever, and stack.start() waits with no diagnostic (the no-timeout behavior noted in #5787 makes this a silent hang).
Verified fix: re-running the identical container with only DB_IP_VERSION=ipv4 added makes migrations run instantly. This looks like the known realtime self-host IPv6-preference gotcha; the ServiceDef probably wants DB_IP_VERSION=ipv4 for the host.docker.internal path.
Worth noting the docker-mode e2e suite doesn't enable realtime, so this path appears never to have been exercised.
2. The ApiProxy can't carry realtime websockets.
The proxy routes /realtime/v1/* as plain HTTP (ApiProxy.ts) and neither it nor the Node platform layer (node.ts — bare createServer()) registers any upgrade handling, so supabase-js — which derives wss://…/realtime/v1/websocket from the project URL — can never connect through stack.url.
3. The direct service endpoint can't serve supabase-js either.
Bypassing the proxy fails independently three ways: the bare realtime server 404s /realtime/v1/websocket (it serves /socket/websocket; the /realtime/v1 prefix is proxy vocabulary), tenant resolution is Host-based (bare-IP → 401 {"message":"Tenant not found in database"}; Host: realtime-dev → 200), and the apikey must be a real JWT since the proxy's publishable-key→JWT transform is HTTP-only. A raw handshake at /socket/websocket with a tenant-bearing hostname and a signed anon JWT does return 101 — so websocket upgrade support in the proxy (path rewrite + tenant Host + the existing auth transform) would make supabase-js work end-to-end with no client changes.
4. Observation, not root-caused: with finding 1 worked around and a 101 handshake established, channel joins still fail with UnableToConnectToTenantDatabase / UnableToConnectToProject — the SEED_SELF_HOST tenant's own (encrypted-settings) connection pool appears not to honor the env-level IPv4 fix. Happy to provide the full repro.
Suggested resolutions: DB_IP_VERSION=ipv4 (or equivalent) in the realtime ServiceDef; websocket upgrade support in the ApiProxy; realtime coverage in the docker-mode e2e.
Pinned at:
develop@31d6fb97· macOS arm64 (Darwin 25.5), Docker Desktop (server 29.5.3), Node 24.18.0, realtime imagev2.112.3(DEFAULT_VERSIONS), consuming@supabase/stackviacreateStack().Enabling realtime in docker mode (
createStack({ mode: "docker", realtime: { port } })) never reaches ready, and even a healthy realtime container can't be consumed by supabase-js through the stack. Three findings, plus one observation:1. The realtime container can't reach postgres —
start()hangs indefinitely.The realtime ServiceDef's env (
packages/stack/src/services/realtime.ts) doesn't setDB_IP_VERSION. On Docker Desktop for Mac, every connection attempt fails with:— while
getent ahosts host.docker.internalinside the same image resolves IPv4-only (192.168.65.254), and GoTrue/PostgREST connect fine over the identical wiring. The migration runner gives up after ~15s, the container exits,unless-stoppedrestart-loops it forever, andstack.start()waits with no diagnostic (the no-timeout behavior noted in #5787 makes this a silent hang).Verified fix: re-running the identical container with only
DB_IP_VERSION=ipv4added makes migrations run instantly. This looks like the known realtime self-host IPv6-preference gotcha; the ServiceDef probably wantsDB_IP_VERSION=ipv4for thehost.docker.internalpath.Worth noting the docker-mode e2e suite doesn't enable realtime, so this path appears never to have been exercised.
2. The ApiProxy can't carry realtime websockets.
The proxy routes
/realtime/v1/*as plain HTTP (ApiProxy.ts) and neither it nor the Node platform layer (node.ts— barecreateServer()) registers anyupgradehandling, so supabase-js — which deriveswss://…/realtime/v1/websocketfrom the project URL — can never connect throughstack.url.3. The direct service endpoint can't serve supabase-js either.
Bypassing the proxy fails independently three ways: the bare realtime server 404s
/realtime/v1/websocket(it serves/socket/websocket; the/realtime/v1prefix is proxy vocabulary), tenant resolution is Host-based (bare-IP →401 {"message":"Tenant not found in database"};Host: realtime-dev→ 200), and theapikeymust be a real JWT since the proxy's publishable-key→JWT transform is HTTP-only. A raw handshake at/socket/websocketwith a tenant-bearing hostname and a signed anon JWT does return101— so websocket upgrade support in the proxy (path rewrite + tenant Host + the existing auth transform) would make supabase-js work end-to-end with no client changes.4. Observation, not root-caused: with finding 1 worked around and a
101handshake established, channel joins still fail withUnableToConnectToTenantDatabase/UnableToConnectToProject— theSEED_SELF_HOSTtenant's own (encrypted-settings) connection pool appears not to honor the env-level IPv4 fix. Happy to provide the full repro.Suggested resolutions:
DB_IP_VERSION=ipv4(or equivalent) in the realtime ServiceDef; websocket upgrade support in the ApiProxy; realtime coverage in the docker-mode e2e.