GUIDE

How to Diff Against a Local Clone

Clone production, develop locally, then diff - without noise from the services a clone doesn't have.

The SupaForge CLI is open source and on npm: npm install -g @akal/supaforge. A hosted dashboard is on the way - to hear when it opens.

Cloning an Environment

supaforge clone copies a remote environment's database - schema and data, or schema only - into a local PostgreSQL database. It previews first; add --apply to run it, and --start-local to have SupaForge start a local PostgreSQL container with Podman or Docker.

Clone production locally
supaforge clone --env=production --start-local --apply

Afterwards the config's target points at the clone, so a plain diff --apply writes to your local copy rather than to the remote.

The Post-Clone Drift Problem

A clone is plain PostgreSQL. Several parts of a Supabase project live in services around the database, and a clone has none of them:

  • storage - buckets and their policies.
  • auth - the auth service's configuration.
  • edge-functions - deployed functions.
  • vault - Supabase Vault secrets.
  • realtime - Realtime publications and channel policies.
  • roles - Supabase's own roles and their grants.

Diffing a clone against the remote reports those checks as drift every time - not because anything is wrong, but because the clone can't hold them. What you want to track locally is the database: schema, RLS, reference data and so on.

Suppress for a Single Run with --skip

--skip (short: -x) leaves a check out of one run, and can be repeated:

Skip Supabase-only checks for this run
supaforge diff --skip=storage --skip=auth --skip=edge-functions --skip=vault --skip=realtime --skip=roles

It combines with every other flag:

Detail view, skipping Supabase-only checks
supaforge diff --detail --skip=storage --skip=auth --skip=edge-functions --skip=vault --skip=realtime --skip=roles

Make It Permanent with checks.exclude

Add a checks.exclude array to supaforge.config.json and those checks are skipped on every run:

supaforge.config.json - permanent exclude list
{
  "environments": { ... },
  "source": "local",
  "target": "production",
  "checks": {
    "exclude": ["storage", "auth", "edge-functions", "vault", "realtime", "roles"]
  }
}

Excluded checks never appear in the output and never count towards the drift score. checks.exclude can also be set on one environment, where it applies whenever that environment is the target. Commit the config so every teammate and CI job uses the same list.

Pushing Local Changes Back

With the clone as the source, --apply makes the remote match the clone - including what the clone doesn't have. SupaForge notices when the source is a clone and says so, and it holds back anything destructive until you add --allow-destructive. Always look at the plan first:

1. Clone and develop
supaforge clone --env=production --apply
# to push local work back, make the clone the "source" and production the "target",
# then make your changes locally: schema, RLS policies, reference data…
2. See what changed
supaforge diff
3. Read the plan
supaforge diff --apply --dry-run
4. Rehearse and apply
supaforge diff --apply --prove

For changes you want reviewed and versioned, supaforge migrate create writes the drift to a migration file instead, and supaforge migrate run applies it.

Install

Install SupaForge
npm install -g @akal/supaforge

Summary

A clone has the database but not Supabase's services. Skip storage, auth, Edge Functions, Vault, Realtime and roles with --skip for one run, or with checks.exclude for good, and read the plan with --dry-run before pushing local changes to a shared environment.

Ready to try SupaForge?

Install the CLI and run your first diff in a minute. Nothing is changed until you add --apply.

npm install -g @akal/supaforge
View on GitHub →