This guide helps you enable CloudSync on a fresh or existing self-hosted Supabase instance. CloudSync adds offline-first synchronization capabilities to your PostgreSQL database.
When deploying or updating your Supabase instance, use the published CloudSync Supabase image instead of the standard Supabase Postgres image.
Follow Supabase's Installing Supabase guide to set up the initial files and .env configuration. Then, before the first docker compose up -d, update your docker-compose.yml to use the CloudSync-enabled Postgres image:
db:
# Supabase on PostgreSQL 15
image: sqlitecloud/sqlite-sync-supabase:15
# instead of: public.ecr.aws/supabase/postgres:15.8.1.085
# OR Supabase on PostgreSQL 17
image: sqlitecloud/sqlite-sync-supabase:17
# instead of: public.ecr.aws/supabase/postgres:17.6.1.071Use the CloudSync image tag that matches your Supabase PostgreSQL major version. The published major tags sqlitecloud/sqlite-sync-supabase:15 and sqlitecloud/sqlite-sync-supabase:17 are the standard choice. Exact Supabase base-image tags may also be published for some releases, but they are optional and not required for normal setup.
Create the init SQL:
mkdir -p volumes/db
cat > volumes/db/cloudsync.sql << 'EOF'
CREATE EXTENSION IF NOT EXISTS cloudsync;
EOFAdd a volume mount to the db service in docker-compose.yml:
services:
db:
volumes:
# ... existing volume mounts ...
- ./volumes/db/cloudsync.sql:/docker-entrypoint-initdb.d/init-scripts/100-cloudsync.sql:ZThe 100- prefix ensures CloudSync loads after Supabase's own init scripts, which are typically numbered 98-99 in the self-hosted Docker Compose setup.
Then start Supabase:
docker compose pull
docker compose up -dFollow Supabase's Updating guide. When updating the Postgres image, replace the default image with the matching CloudSync image:
# Update docker-compose.yml to use:
# sqlitecloud/sqlite-sync-supabase:15
# or sqlitecloud/sqlite-sync-supabase:17
docker compose pull
docker compose down && docker compose up -dIf Postgres has already been initialized and you are adding CloudSync afterward, the init script will not run automatically. Connect to the database and run:
CREATE EXTENSION IF NOT EXISTS cloudsync;docker compose exec db psql -U supabase_admin -d postgres -c "SELECT cloudsync_version();"If the extension is installed correctly, PostgreSQL returns the CloudSync version string.
CloudSync uses the first two components of its semver as the PostgreSQL extension version (for example, 1.0.17 installs as extension version 1.0). How you upgrade depends on which component changed:
-
PATCH release (e.g.
1.0.17 → 1.0.18): pull the matchingsqlitecloud/sqlite-sync-supabase:<tag>image and restart thedbservice. No SQL-level upgrade is needed —installed_versionstays at1.0and the new binary takes over on reconnect.SELECT cloudsync_version();confirms the new semver. -
MINOR or MAJOR release (e.g.
1.0.x → 1.1.0): pull the new image and restart as above, then run once per database:ALTER EXTENSION cloudsync UPDATE;PostgreSQL applies any
cloudsync--<from>--<to>.sqlupgrade scripts shipped with the release and movesinstalled_versionto the new value.
You can check the current state at any time:
SELECT name, default_version, installed_version
FROM pg_available_extensions
WHERE name = 'cloudsync';If installed_version is behind default_version after a release, run ALTER EXTENSION cloudsync UPDATE; to catch up.
In the CloudSync dashboard, create a new workspace with the Supabase (Self-hosted) provider, then add a project with your PostgreSQL connection string:
postgresql://user:password@host:5432/database
In the dashboard, go to the Database Setup tab, select the tables you want to sync, and click Deploy Changes.
On the Client Integration tab you'll find your Database ID and authentication settings.
The fastest way to test CloudSync without per-user access control — no JWT setup needed.
With API key authentication, CloudSync uses the database role resolved from the API-key-authenticated connection when available; otherwise it falls back to the role from the connection string.
SELECT cloudsync_network_init('<database-id>');
SELECT cloudsync_network_set_apikey('<username>:<password>');
SELECT cloudsync_network_sync();- Set Row Level Security to Yes, enforce RLS
- Under Authentication (JWT), click Configure authentication and choose:
- HMAC Secret (HS256):
- Enter your
JWT_SECRETfrom Supabase's.env - Optionally add Expected audiences. When configured, a token's
audclaim must contain at least one of the configured audience values.
- Enter your
- JWKS Issuer Validation:
- Enter the issuer base URL from your token's
issclaim (for examplehttps://your-auth-domain) - By default, CloudSync uses OIDC discovery: it requests
<issuer>/.well-known/openid-configurationand reads the returnedjwks_uri - Optionally set an Explicit JWKS URI to bypass OIDC discovery and use a specific JWKS endpoint directly. This must be a full HTTPS URI.
- Optionally add Expected audiences. When configured, a token's
audclaim must contain at least one of the configured audience values.
- Enter the issuer base URL from your token's
- HMAC Secret (HS256):
- CloudSync validates JWTs as follows:
- HS256: uses the configured JWT secret
- JWKS: uses the explicit
jwksUriwhen provided; otherwise CloudSync requests<issuer>/.well-known/openid-configurationand readsjwks_uri - CloudSync does not fall back directly to
<issuer>/.well-known/jwks.jsonwhen discovery is used
- For claim details and RLS examples, see:
- In your client code:
SELECT cloudsync_network_init('<database-id>'); SELECT cloudsync_network_set_token('<jwt-token>'); SELECT cloudsync_network_sync();