Edge runtime container lookup fails due to non-DNS-compliant container names
Overview
With a Supabase local dev setup, Edge functions are unable to resolve container names, such as those provided by the SUPABASE_DB_URL environment variable. As CLI's generated container hostnames currently contain underscores, which Deno's strict resolver rejects due to non-compliance with RFC 1123 (only letters, numbers, and hyphens).
To Reproduce
-
mkdir cloud && cd cloud -
supabase init -
supabase functions new hello - Modify
supabase/config.toml
Under[functions.hello], setverify_jwt = false -
supabase start
Replace contents of supabase/functions/hello/index.ts with:
import postgres from 'npm:postgres'
const connectionString = Deno.env.get('SUPABASE_DB_URL')!
Deno.serve(async (_req) => {
const output = [connectionString]
try {
const sql = postgres(connectionString, { prepare: false })
output.push(`select 1 = ${JSON.stringify(await sql`select 1`)}`)
} catch (e) {
output.push(`sql error! - ${e}`)
if (e instanceof Error && e.stack) output.push(e.stack)
}
return new Response(output.join('\n'))
})
Based on Supabase tutorial & Drizzle tutorial.
Expected behavior
postgresql://postgres:postgres@supabase_db_cloud:5432/postgres
select 1 = [{"?column?":1}]
Current behavior
postgresql://postgres:postgres@supabase_db_cloud:5432/postgres
sql error! - Error: getaddrinfo ENOTFOUND supabase_db_cloud
Error: getaddrinfo ENOTFOUND supabase_db_cloud
at __node_internal_captureLargerStackTrace (ext:deno_node/internal/errors.ts:93:9)
at __node_internal_ (ext:deno_node/internal/errors.ts:246:10)
at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:37:26)
at ext:deno_node/internal_binding/cares_wrap.ts:78:9
at eventLoopTick (ext:core/01_core.js:207:9)
at cachedError (file:///var/tmp/sb-compile-edge-runtime/node_modules/localhost/postgres/3.4.7/src/query.js:170:23)
at new Query (file:///var/tmp/sb-compile-edge-runtime/node_modules/localhost/postgres/3.4.7/src/query.js:36:24)
at sql (file:///var/tmp/sb-compile-edge-runtime/node_modules/localhost/postgres/3.4.7/src/index.js:112:11)
at Object.handler (file:///Users/adapt/tmp/repro/cloud/supabase/functions/hello/index.ts:12:55)
System information
- OS: MacOS 15.6
- CLI: 2.34.3
- Docker: 4.44.2 (202017)
- Services:
SERVICE IMAGE | LOCAL | LINKED
------------------------|------------------------|--------
supabase/postgres | 17.4.1.072 | -
supabase/gotrue | v2.178.0 | -
postgrest/postgrest | v13.0.4 | -
supabase/realtime | v2.41.23 | -
supabase/storage-api | v1.26.3 | -
supabase/edge-runtime | v1.68.3 | -
supabase/studio | 2025.08.04-sha-6e99ca6 | -
supabase/postgres-meta | v0.91.5 | -
supabase/logflare | 1.18.3 | -
supabase/supavisor | 2.6.1 | -
Container names generated as part of a supabase start
NAMES
supabase_studio_cloud
supabase_pg_meta_cloud
supabase_edge_runtime_cloud
supabase_storage_cloud
supabase_rest_cloud
supabase_realtime_cloud
supabase_inbucket_cloud
supabase_auth_cloud
supabase_kong_cloud
supabase_vector_cloud
supabase_analytics_cloud
supabase_db_cloud
Other observations
Using db as the hostname works
postgresql://postgres:postgres@db:5432/postgres
select 1 = [{"?column?":1}]
Pinging from within the container is ok
docker exec -it supabase_edge_runtime_cloud /bin/sh -c 'apt update && apt install iputils-ping'
docker exec -it supabase_edge_runtime_cloud ping -c 1 supabase_db_cloud
PING supabase_db_cloud (172.20.0.2) 56(84) bytes of data.
64 bytes from supabase_db_cloud.supabase_network_cloud (172.20.0.2): icmp_seq=1 ttl=64 time=0.054 ms
Rolling up the sleeves
docker run --network supabase_network_cloud --name test-test -dt debian
docker run --network supabase_network_cloud --name test_test -dt debian
Update supabase/functions/hello/index.ts:
import { lookup } from "node:dns/promises"
const doLookup = async (host: string) => {
try {
return `lookup("${host}") ${(await lookup(host)).address}`
} catch (e) {
return `lookup("${host}") ${e}`
}
}
Deno.serve(async (_req) => {
const output = [
await doLookup(URL.parse(Deno.env.get('SUPABASE_DB_URL')!)?.hostname!),
await doLookup('db'),
await doLookup('test_test'),
await doLookup('test-test'),
await doLookup('fail'),
]
return new Response(output.join('\n'))
})
Outputs:
lookup("supabase_db_cloud") Error: getaddrinfo ENOTFOUND supabase_db_cloud
lookup("db") 172.20.0.2
lookup("test_test") Error: getaddrinfo ENOTFOUND test_test
lookup("test-test") 172.20.0.14
lookup("fail") Error: getaddrinfo ENOTFOUND fail
i.e., can resolve hostnames complying with RFC 1123
Workaround
Rewrite the hostname in the url supabase_db_cloud -> db
const connectionString = (() => {
const url = URL.parse(Deno.env.get('SUPABASE_DB_URL')!)!
url.hostname = url.hostname.split('_')[1]
return url.href
})();
Soap box
While technically this is more of a compatibility between Docker's free for all container naming, ping be lenient on what it'll resolve, and Deno's strict/compliant resolver.
I feel there is a large value in addressing this in Supabase CLI.
Proposed solution
Use RFC 1123 compliant container names as part of supabase start
This would resolve the initial SUPABASE_DB_URL issue, as well as likely other issues when resolving container hostnames from Edge functions.
i.e., supabase_db_cloud -> supabase-db-cloud
Keeping in mind that the project_id in config.toml will need to be sanitized too.
We use container aliases for predictable hostname resolution https://github.com/supabase/cli/blob/e3d22ada3db8d89fc0790d97bcbd89b9f2f4db1d/internal/utils/config.go#L36
The only non-compliant alias is edge_runtime but since deno runs in it, there might not be an urgent need to change it.
A supbase_ prefix and _{project} postfix is then used, which makes them all non-compliant:
https://github.com/supabase/cli/blob/e3d22ada3db8d89fc0790d97bcbd89b9f2f4db1d/internal/utils/config.go#L65-L68
https://github.com/supabase/cli/blob/e3d22ada3db8d89fc0790d97bcbd89b9f2f4db1d/internal/utils/config.go#L57-L59
Curiously lookup is fine if I run within a deno environment directly:
docker run -it --name db-test --network test denoland/deno repl
docker run -it --name db_test --network test denoland/deno repl
import { lookup } from "node:dns/promises"
await lookup('db_test')
# { address: "172.20.0.3", family: 4 }
await lookup('db-test')
# { address: "172.20.0.2", family: 4 }
Suggesting its something with edge functions?