diff --git a/apps/cli-go/internal/db/start/start.go b/apps/cli-go/internal/db/start/start.go index 6cd411791c..7ba19afbc8 100644 --- a/apps/cli-go/internal/db/start/start.go +++ b/apps/cli-go/internal/db/start/start.go @@ -274,6 +274,8 @@ func initRealtimeJob(host, jwks string) utils.DockerJob { "DB_PORT=5432", "DB_USER=" + utils.SUPERUSER_ROLE, "DB_PASSWORD=" + utils.Config.Db.Password, + "DB_USER_REALTIME=supabase_realtime_admin", + "DB_PASS_REALTIME=" + utils.Config.Db.Password, "DB_NAME=postgres", "DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime", "DB_ENC_KEY=" + utils.Config.Realtime.EncryptionKey, diff --git a/apps/cli-go/internal/db/start/templates/schema.sql b/apps/cli-go/internal/db/start/templates/schema.sql index dee166ae58..36f2b79d49 100644 --- a/apps/cli-go/internal/db/start/templates/schema.sql +++ b/apps/cli-go/internal/db/start/templates/schema.sql @@ -13,5 +13,17 @@ ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_replication_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_read_only_user WITH PASSWORD :'pgpass'; +-- supabase_realtime_admin is created by realtime's own tenant migrations on first +-- boot, so it does not exist in the base image. Create it up front (idempotently) +-- so realtime can authenticate as a least-privileged user from the start. +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'supabase_realtime_admin') THEN + CREATE ROLE supabase_realtime_admin WITH NOINHERIT CREATEROLE LOGIN REPLICATION; + END IF; +END +$$; +ALTER USER supabase_realtime_admin WITH PASSWORD :'pgpass'; + create schema if not exists _realtime; alter schema _realtime owner to postgres; diff --git a/apps/cli-go/internal/start/start.go b/apps/cli-go/internal/start/start.go index a6e1a30498..8ec327cd37 100644 --- a/apps/cli-go/internal/start/start.go +++ b/apps/cli-go/internal/start/start.go @@ -858,6 +858,8 @@ EOF fmt.Sprintf("DB_PORT=%d", dbConfig.Port), "DB_USER=" + utils.SUPERUSER_ROLE, "DB_PASSWORD=" + dbConfig.Password, + "DB_USER_REALTIME=supabase_realtime_admin", + "DB_PASS_REALTIME=" + dbConfig.Password, "DB_NAME=" + dbConfig.Database, "DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime", "DB_ENC_KEY=" + utils.Config.Realtime.EncryptionKey, diff --git a/apps/cli-go/pkg/config/templates/Dockerfile b/apps/cli-go/pkg/config/templates/Dockerfile index 0c59cf9f3a..3b2940ca5a 100644 --- a/apps/cli-go/pkg/config/templates/Dockerfile +++ b/apps/cli-go/pkg/config/templates/Dockerfile @@ -11,7 +11,7 @@ FROM supabase/edge-runtime:v1.74.1 AS edgeruntime FROM timberio/vector:0.53.0-alpine AS vector FROM supabase/supavisor:2.9.7 AS supavisor FROM supabase/gotrue:v2.190.0 AS gotrue -FROM supabase/realtime:v2.108.0 AS realtime +FROM supabase/realtime:v2.109.1 AS realtime FROM supabase/storage-api:v1.60.22 AS storage FROM supabase/logflare:1.44.3 AS logflare # Append to JobImages when adding new dependencies below diff --git a/packages/stack/src/StackBuilder.unit.test.ts b/packages/stack/src/StackBuilder.unit.test.ts index 953fd7c01a..17cf6d6a75 100644 --- a/packages/stack/src/StackBuilder.unit.test.ts +++ b/packages/stack/src/StackBuilder.unit.test.ts @@ -456,8 +456,9 @@ describe("StackBuilder", () => { }); const realtimeDef = graph.startOrder.find((service) => service.name === "realtime"); - expect(realtimeDef?.args).toContain("supabase/realtime:v2.78.10"); - expect(realtimeDef?.args).not.toContain("public.ecr.aws/supabase/realtime:v2.78.10"); + const realtimeTag = `supabase/realtime:v${DEFAULT_VERSIONS.realtime}`; + expect(realtimeDef?.args).toContain(realtimeTag); + expect(realtimeDef?.args).not.toContain(`public.ecr.aws/${realtimeTag}`); }).pipe(Effect.provide(layer)); }); }); diff --git a/packages/stack/src/services/postgres-init.ts b/packages/stack/src/services/postgres-init.ts index 694951230d..b0826902f9 100644 --- a/packages/stack/src/services/postgres-init.ts +++ b/packages/stack/src/services/postgres-init.ts @@ -114,9 +114,15 @@ EOSQL ${psql} -U supabase_admin -d postgres -c " DO \\$\\$ DECLARE - roles text[] := ARRAY['authenticator','supabase_auth_admin','supabase_storage_admin','supabase_functions_admin','supabase_replication_admin','supabase_read_only_user','postgres']; + roles text[] := ARRAY['authenticator','supabase_auth_admin','supabase_storage_admin','supabase_functions_admin','supabase_replication_admin','supabase_read_only_user','supabase_realtime_admin','postgres']; r text; BEGIN + -- supabase_realtime_admin is created later by realtime's own tenant migrations, + -- so it is missing from the base image. Create it up front so the password loop + -- below can set its credentials and realtime can connect as a least-privileged user. + IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'supabase_realtime_admin') THEN + CREATE ROLE supabase_realtime_admin WITH NOINHERIT CREATEROLE LOGIN REPLICATION; + END IF; FOREACH r IN ARRAY roles LOOP IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = r) THEN EXECUTE format('ALTER ROLE %I WITH PASSWORD ''postgres''', r); diff --git a/packages/stack/src/services/postgres.ts b/packages/stack/src/services/postgres.ts index bb3b5953f7..f3ce25c43f 100644 --- a/packages/stack/src/services/postgres.ts +++ b/packages/stack/src/services/postgres.ts @@ -64,6 +64,14 @@ ALTER USER supabase_auth_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_replication_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_read_only_user WITH PASSWORD :'pgpass'; +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'supabase_realtime_admin') THEN + CREATE ROLE supabase_realtime_admin WITH NOINHERIT CREATEROLE LOGIN REPLICATION; + END IF; +END +$$; +ALTER USER supabase_realtime_admin WITH PASSWORD :'pgpass'; create schema if not exists _realtime; alter schema _realtime owner to postgres; SELECT 'CREATE DATABASE _supabase WITH OWNER postgres' diff --git a/packages/stack/src/services/realtime.ts b/packages/stack/src/services/realtime.ts index 4f4c209777..d423f346a9 100644 --- a/packages/stack/src/services/realtime.ts +++ b/packages/stack/src/services/realtime.ts @@ -48,6 +48,8 @@ export const makeRealtimeServiceDocker = (opts: DockerRealtimeOptions): ServiceD DB_PORT: String(opts.dbPort), DB_USER: "postgres", DB_PASSWORD: "postgres", + DB_USER_REALTIME: "supabase_realtime_admin", + DB_PASS_REALTIME: "postgres", DB_NAME: "postgres", DB_AFTER_CONNECT_QUERY: "SET search_path TO _realtime", DB_ENC_KEY: opts.encryptionKey, diff --git a/packages/stack/src/versions.ts b/packages/stack/src/versions.ts index 3484f01d56..00ac5da731 100644 --- a/packages/stack/src/versions.ts +++ b/packages/stack/src/versions.ts @@ -50,7 +50,7 @@ export const DEFAULT_VERSIONS: VersionManifest = { postgrest: "14.5", auth: "2.188.0-rc.15", "edge-runtime": "1.73.13", - realtime: "2.78.10", + realtime: "2.109.1", storage: "1.41.8", imgproxy: "v3.8.0", mailpit: "v1.22.3",