Make self-host docker compose up -d actually work (production build, Kong keys, API-role grants)#160
Merged
Merged
Conversation
…n stack
The documented one-command self-host was non-functional on a fresh box. Three
defects, each reproduced and fixed against a live boot:
- The git-tracked docker-compose.override.yml auto-loaded on `docker compose up`
and swapped the web app to the Next.js dev server (target: deps, pnpm dev,
NODE_ENV=development) while exposing Postgres on the host. Renamed to
docker-compose.dev.yml so the dev overlay is opt-in; the default command now
builds and serves the production image (node server.js).
- supabase-kong mounted docker/kong.yml with literal ${ANON_KEY}/${SERVICE_ROLE_KEY}
placeholders that nothing substituted, so key-auth registered the placeholders
and rejected the real generated keys; every API call (auth/REST/storage) failed
with "Invalid authentication credentials". Kong now renders the key template at
startup; kong.yml quotes converted to single so the substitution is safe.
- run-self-host-migrations.sh did not establish the API-role grants for objects
created as POSTGRES_USER (docker/init's defaults are keyed to a different role),
so only a handful of tables were reachable and setup failed with 42501. Sets
ALTER DEFAULT PRIVILEGES before the migration loop so each migration's own
hardening REVOKE stays final, and enables RLS on the internal ledger.
README: correct the "nothing else to install" claim (add a Docker-only env-gen
path), document the opt-in dev overlay, note the production build.
verify-self-host-migrations.test.mjs guards all three against regression (CI).
Verified on a fresh `docker compose up -d` over wiped volumes: setup, sign-in,
and an authed data read all succeed; profile role-escalation and retro
facilitator-token hardening remain intact.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Following the schema-on-first-boot fix (#159), I ran the documented self-host flow one command at a time on a fresh clone and booted the real stack.
docker compose up -dproduced a non-functional instance. Three defects, each reproduced and fixed against a live boot:1. The default command ran the dev server (and exposed Postgres)
docker-compose.override.ymlwas git-tracked, so Docker Compose auto-merged it on everydocker compose up. It swapped the web service totarget: deps+pnpm dev+NODE_ENV=development(a crashing Next.js dev server, restarted 3x in my boot) and bound Postgres to0.0.0.0:5432. The Dockerfile's productionrunnerstage (node server.js) was never used on the default path.Fix: renamed the overlay to
docker-compose.dev.yml(opt-in:docker compose -f docker-compose.yml -f docker-compose.dev.yml up). The defaultdocker compose up -dnow builds and serves the production image, and no longer exposes the DB port.2. Kong rejected the real API keys
docker/kong.ymlshipped literal${ANON_KEY}/${SERVICE_ROLE_KEY}placeholders; nothing substituted them, so key-auth registered the placeholder strings and every API request (auth, REST, storage) failed withInvalid authentication credentials. The app could not sign in or read data.Fix: the
supabase-kongservice renders the key template at startup (canonical Supabase pattern), substituting the generated keys.docker/kong.ymldouble quotes converted to single so the shell substitution is safe.3. Most tables were unreachable by the API roles
The self-host psql migrate path created objects as
POSTGRES_USER, anddocker/init's default privileges are keyed to a different role, so only a handful of the 26 tables were granted toanon/authenticated/service_role.create-adminfailed with42501 permission denied for table profiles, blocking setup.Fix:
run-self-host-migrations.shsetsALTER DEFAULT PRIVILEGESforPOSTGRES_USERbefore the migration loop (mirroring the platform), so every migration-created object is granted at creation and each migration's own hardeningREVOKEstays the final word. The internal ledger gets RLS enabled and is intentionally excluded from grants.Docs
Guards (run in CI via
test:self-host)verify-self-host-migrations.test.mjsnow also asserts: no auto-loaded dev override; the default web service is production; Kong substitutes keys;kong.ymlis substitution-safe; and default privileges run before the loop with no blanketGRANT ... ON ALL(the exact regression that caused the security holes). 8/8 pass.Verification
A fresh
docker compose up -dover wiped volumes: migrations apply, Kong serves real keys, and the full flow passes —create-admin->seed-demo->complete-> password sign-in -> authenticated PostgREST read of the seeded series ->GET / 200. Hardening intact:authenticatedhas no table-level UPDATE onprofiles(role/has_full_access not grantable) andanoncannot EXECUTE any internal retro helper.CI (
supabase start) is unaffected:docker/kong.ymlandrun-self-host-migrations.share only used by the compose self-host path.