Skip to content

Guide · Supabase to HornetHiveMigrate from Supabase to HornetHive

A Supabase project is a PostgreSQL database with services around it. The database moves to HornetHive with pg_dump and pg_restore; the services do not. This guide shows what comes over, the commands that work, and the few things to change in your app.

Tools
pg_dump · pg_restore
Needs
Pro plan
Keeps
Tables · data · keys

What moves to HornetHive, and what does not?

Everything in your public schema moves: tables, rows, primary and foreign keys, indexes, sequences and identity columns, defaults, functions and triggers. Your Supabase users can come along as a plain auth.users table, so foreign keys to it keep working.

What stays behind is what Supabase runs around Postgres:

  • Supabase Auth: sign-up, login and sessions for your app's users. HornetHive does not log in your users; your app or another auth service does.
  • Row Level Security policies built on auth.uid() and the anon and authenticated roles. They read Supabase's JWT, which HornetHive does not issue.
  • Storage (files), Realtime and Edge Functions.
  • Extensions outside PostgreSQL's own contrib set, such as pgvector, PostGIS, pg_cron and pg_net. Contrib extensions such as pgcrypto, uuid-ossp, pg_trgm, citext, hstore and ltree are available, and the database owner can create them.

What do you need before you start?

A HornetHive database on the Pro plan, which comes with the direct connection string that pg_restore needs. Create the database, then copy its direct connection string from the Direct connection tab.

PostgreSQL 18 client tools (pg_dump, pg_restore, psql) on the machine you run the move from. pg_dump must be at least the version of the server it reads, and 18 reads every Supabase version.

Your Supabase connection string: in the Supabase dashboard, Connect, then Session pooler (port 5432). The transaction pooler (port 6543) does not suit pg_dump. Before you move, check that no column in public uses a type from an extension HornetHive lacks, such as pgvector's vector:

-- run on Supabase: columns whose type comes from an extension
SELECT table_name, column_name, udt_schema, udt_name
FROM information_schema.columns
WHERE table_schema = 'public' AND udt_schema NOT IN ('pg_catalog', 'public');
HornetHive plans compared, in Ringgit
PlanPrice (RM / month)BilledDatabasesStorageAPI calls / monthREST & GraphQLMCPDirect connection
Free0.00Free forever1500 MB500KYes——
Starter7.00Yearly, RM 84.0035 GB2.5MYesYes—
Pro29.00Yearly, RM 348.001020 GB25MYesYesYes

Can an AI agent do the migration for you?

Yes, if the agent can run commands on your machine: Claude Code, Cursor, Codex or another coding agent with a terminal. It creates the HornetHive database through the HornetHive MCP server, then runs the pg_dump and pg_restore steps below in the terminal and checks the result. There is no single MCP tool that copies the data, because MCP has no tool that writes rows; the terminal does that part.

Before you start: PostgreSQL 18 client tools installed, the Pro plan, and an API key with the write scope (a read key is not offered create_database) with Allowed IPs left at *. Add the MCP server to your agent, and export your Supabase connection string before you start the agent, so the agent's terminal has it and it never goes into the chat:

claude mcp add --transport http hive https://hornethive.io/mcp \
  --header "Authorization: Bearer YOUR_WRITE_API_KEY"

export SUPABASE_URL='postgresql://postgres.<ref>:<password>@<region>.pooler.supabase.com:5432/postgres'

What should you tell the agent?

Copy this prompt, fill in the label, region and password, and paste it into the agent. It asks the agent to follow this guide, keep your Supabase project unchanged, keep passwords out of its output, and stop for you at the two points that matter: before it creates the database, and if a column uses a type HornetHive does not have.

Migrate my Supabase database to HornetHive by following
https://hornethive.io/migrate-from-supabase step by step.

- Supabase: the Session pooler connection string is in $SUPABASE_URL.
  Only read from it; change nothing in Supabase.
- Use PostgreSQL 18 client tools (check pg_dump --version first).
- First run the guide's extension check on Supabase. If any public column
  uses a type HornetHive lacks (pgvector, PostGIS, ...), stop and tell me.
- With the hive MCP server: list_regions, then tell me what you will create
  and wait for my yes. Then create_database with label "<label>",
  region "<my or jp>" and password "<a strong password>".
- get_connection_string masks the password: build the direct connection
  string with my password and export it as $HIVE_URL. Never print it.
- Do steps 1 to 4 of the guide exactly: prepare the schemas and extensions,
  make the two dumps, restore with the list files, then compare row counts
  table by table between Supabase and HornetHive.
- Show me every pg_restore error and say which are expected (views or
  functions using auth.uid()) and which need fixing.
- Finish with sync_graphql and list_tables on the new database.

Step 1: how do you prepare the HornetHive database?

Supabase keeps extensions in a schema named extensions, and column defaults name it, as in extensions.uuid_generate_v4(). Create that schema and the extensions your tables use, plus an empty auth schema for the users table. Run this once with the direct connection string:

export SUPABASE_URL='postgresql://postgres.<ref>:<password>@<region>.pooler.supabase.com:5432/postgres'
export HIVE_URL='<HornetHive direct connection string>'

psql "$HIVE_URL" -c 'CREATE SCHEMA IF NOT EXISTS extensions; CREATE SCHEMA IF NOT EXISTS auth;' \
  -c 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions;' \
  -c 'CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions;'

Step 2: how do you dump from Supabase?

Make two dumps: one of your public schema, and one of auth.users alone. They must be separate, because pg_dump ignores --schema when --table is given. Dumping only reads; your Supabase project is not changed.

pg_dump "$SUPABASE_URL" --table=auth.users --no-owner --no-privileges --format=custom -f auth_users.dump
pg_dump "$SUPABASE_URL" --schema=public   --no-owner --no-privileges --format=custom -f public.dump

Step 3: how do you restore into HornetHive?

Restore auth.users first, so foreign keys to it can be created. Leave out what cannot work outside Supabase: the public schema itself (it already exists), Row Level Security and its policies, Realtime publications, and the triggers on auth.users, which only Supabase Auth fires. pg_restore's list file does that:

pg_restore -l auth_users.dump | grep -vE ' (TRIGGER|ROW SECURITY) ' > auth.list
pg_restore -l public.dump | grep -vE ' (SCHEMA - public|COMMENT - SCHEMA public|POLICY|ROW SECURITY|PUBLICATION TABLE|PUBLICATION) ' > public.list

pg_restore --no-owner --no-privileges -L auth.list   -d "$HIVE_URL" auth_users.dump
pg_restore --no-owner --no-privileges -L public.list -d "$HIVE_URL" public.dump

Step 4: how do you check what came over?

pg_restore prints each statement it could not run and carries on. Expect errors only for objects that call Supabase functions, such as a view or function using auth.uid(): rewrite those for your app, or drop them. Then compare row counts with Supabase, table by table:

psql "$HIVE_URL" -c "SELECT relname AS table, n_live_tup AS rows FROM pg_stat_user_tables ORDER BY relname;"
psql "$HIVE_URL" -c "SELECT count(*) FROM auth.users;"

Step 5: how do you point your app at HornetHive?

If your app reaches Postgres through an ORM or a driver (Prisma, Drizzle, Laravel, Django, Rails, node-postgres), change DATABASE_URL to the pooled connection string, and the migration URL to the direct one. Nothing else changes.

If it uses supabase-js to read tables, use the HornetHive JS SDK or the REST API with an API key instead. The shape is close: select, equality filters, order, limit and offset. Other filters, such as greater-than or LIKE, go through the SQL endpoint with parameters. An API key carries its full scope for whoever holds it, so keep write keys on your server; from a browser, use a read key limited to one database, with your site added under Origins.

// supabase-js
const { data } = await supabase.from('todos').select('id, title').eq('done', false).order('id').limit(20)

// @hornetcloud/hive-js
const todos = await hive.from(DATABASE_ID).from('todos').select('id, title').eq('done', false).order('id').limit(20).get()

// anything beyond equality: SQL with parameters
await fetch(`https://hornethive.io/api/rest/${DATABASE_ID}/query`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.HIVE_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ query: 'SELECT id, title FROM todos WHERE created_at > $1', params: ['2026-01-01'] }),
})

What about Row Level Security and your users' passwords?

Supabase's policies decide access from the logged-in user's JWT. On HornetHive, access is decided by your server and by API key scopes: read or write, for one database or all of them, and optionally only from listed IP addresses. Move the checks your policies made into your server code, where your app already knows who the user is.

auth.users comes over with Supabase's columns, including encrypted_password. If nothing in your app will read it, drop the columns you do not need once the move is checked: ALTER TABLE auth.users DROP COLUMN encrypted_password;

Updated

Questions

Will my users still be able to log in?+

Not through Supabase Auth: HornetHive does not run authentication for your app. Your users' rows come over in auth.users, so an auth service you choose can take them from there.

Does the move change my Supabase project?+

No. pg_dump only reads. Keep Supabase running until your app has switched and you have checked the data.

Why do I need the Pro plan?+

pg_restore needs a direct connection to Postgres, and the direct connection string comes with Pro. REST and GraphQL are on every plan.

pg_restore printed errors. Did the move fail?+

Read them: errors for views or functions that use auth.uid(), or for Supabase-only extensions, are expected and leave the rest of the restore in place. Compare row counts to be sure.

My Supabase project is on PostgreSQL 15 or 17. Can it move to PostgreSQL 18?+

Yes. Moving to a newer major version is what pg_dump and pg_restore are for. Use the version 18 tools: pg_dump 18 reads every older server, and pg_restore 18 reads dumps made by older pg_dump. An older pg_dump cannot read a newer server, so do not dump a PostgreSQL 17 project with pg_dump 16.

Is there an MCP tool that migrates Supabase to HornetHive?+

Not one tool. An AI agent with a terminal does it: the HornetHive MCP server creates the database and checks the tables, and pg_dump and pg_restore copy the data. The prompt on this page asks it to follow this guide.

Do my tables show up in GraphQL?+

Yes, after a sync from the database's GraphQL tab or the sync_graphql MCP tool. REST sees them at once.