stack: realtime service cannot run (or be consumed) in docker mode

Open
#5,788 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
43/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
docker, node.js, postgresql, typescript

Research direction

Start with packages/stack/src/services/realtime.ts to trace Docker startup and the missing database connectivity setting. Then inspect ApiProxy.ts, node.ts, and the docker-mode end-to-end suite for websocket upgrade, routing, and coverage behavior. Done means realtime starts reliably, supabase-js can connect through stack.url, and the docker-mode tests cover the path.

Written by the indexing model from the issue text.

Description

🐛 Bug supabase/cli

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 supabase/cli#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.

Dominant language
TypeScript
Stars
2.4k
Forks
523
Avg merge
21h 33m
Merged PRs (30d)
258

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from supabase/cli

All issues in supabase/cli

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.