Deploy to Supabase
One Hono app, three runtimes. On Supabase the entry is
apps/server/src/entry/deno.ts, the database is the project’s Postgres and the
bucket is Supabase Storage.
| Runtime | Database | Storage |
|---|---|---|
| Edge Function (Deno) | Supabase Postgres | Supabase Storage |
The constraint that shaped the design
Section titled “The constraint that shaped the design”An edge function cannot stream a 50–200 MB request body. That is why publishing
is three steps and the bundle never passes through the API: the CLI hashes the
zip locally, asks for a signed upload URL, PUTs straight to the bucket, then
calls confirm. Only the manifest crosses this process. The same shape is what
keeps a large publish off the critical path on every other target too.
The other consequence: migrations do not run on boot here. supabase db push
owns the schema.
What ota init --provider supabase runs
Section titled “What ota init --provider supabase runs”packages/cli/src/providers/supabase.ts is a flat list of five steps. Each one
is printed before it runs; steps that change remote state ask for confirmation
unless you pass -y. If supabase is not on PATH, or you pass --dry-run,
the whole list is printed and nothing is executed.
npx @open-ota/cli init --provider supabase| Step | Command | Note |
|---|---|---|
| Link this directory to your Supabase project | supabase link --project-ref <project-ref> |
Printed only, never run — a command still holding a <placeholder> is left for you. |
| Push the schema | supabase db push |
Runs the Open OTA migrations against the linked project’s Postgres. |
| Create the bundle bucket | supabase storage create ss:///ota-bundles |
Older CLIs have no storage command; create it in the dashboard (Storage → New bucket, public) if it fails. |
| Set the function secrets | supabase secrets set OTA_MASTER_KEY=… OTA_MODE=self STORAGE_BUCKET=ota-bundles |
The master key is generated by the CLI for this run. |
| Deploy the Edge Function | supabase functions deploy ota --no-verify-jwt |
The function does its own Bearer auth; Supabase JWT verification would reject CLI tokens. |
The provider only provisions. ota init still creates or links the project,
writes ota.config.json and wires the native side, whether or not you pass
--provider.
Doing it by hand
Section titled “Doing it by hand”supabase link --project-ref <ref>supabase db push # migrations in ./drizzlesupabase secrets set OTA_MASTER_KEY=... OTA_MODE=self STORAGE_BUCKET=ota-bundlessupabase functions deploy ota --no-verify-jwt # our own Bearer auth, not Supabase JWTsThe function scaffold
Section titled “The function scaffold”The Supabase CLI bundles the function with Deno’s own resolver, so point it at
this entry — supabase/functions/ota/index.ts:
import "../../../apps/server/src/entry/deno.ts";Deno resolves specifiers literally and this codebase writes .js for .ts
files, so the function needs a deno.json next to it:
{ "unstable": ["sloppy-imports"] }Environment
Section titled “Environment”Set as function secrets. STORAGE_DRIVER=supabase selects the Supabase Storage
adapter, which signs uploads through /storage/v1/object/upload/sign/... and
serves downloads from the public object URL.
| Variable | Value |
|---|---|
DATABASE_URL |
The project’s Postgres connection string. |
OTA_MASTER_KEY |
32 random bytes, base64 (openssl rand -base64 32). |
STORAGE_DRIVER |
supabase |
SUPABASE_URL |
Project URL. Required by this driver. |
SUPABASE_SERVICE_ROLE_KEY |
Service role key. Required by this driver. |
STORAGE_BUCKET |
ota-bundles |
PUBLIC_URL |
The function’s public URL, if you use preview links or remote MCP. |
Full descriptions are on the configuration page.
The dashboard
Section titled “The dashboard”The Edge Function does not serve static files. Run the dashboard locally
against the deployed API with ota console, or deploy apps/dashboard/dist as
a static site.
What is verified
Section titled “What is verified”src/app.ts is identical on all three targets and is covered by the server
test suite, including a run of the real migrations against a real Postgres
engine in-process. The signing code is Web Crypto only, so it runs unchanged
under Deno.