This directory contains Docker configurations for developing and testing CloudSync with PostgreSQL.
docker/
├── postgresql/ # Standalone PostgreSQL with CloudSync
│ ├── Dockerfile # Custom PostgreSQL image
│ ├── docker-compose.yml
│ ├── init.sql # CloudSync metadata tables
│ └── cloudsync.control
Use this for simple PostgreSQL development and testing.
# Build Docker image with CloudSync extension
make postgres-docker-build
# Start PostgreSQL container
make postgres-docker-run
# Test the extension
docker exec -it cloudsync-postgres psql -U postgres -d cloudsync_test -c "CREATE EXTENSION cloudsync; SELECT cloudsync_version();"This starts:
- PostgreSQL 16 on
localhost:5432 - CloudSync extension pre-installed
- pgAdmin on
localhost:5050(optional, use--profile admin)
- Database:
cloudsync_test - Username:
postgres - Password:
postgres
After making changes to the source code:
# Quick rebuild inside running container (fast!)
make postgres-dev-rebuild
# Then reload the extension in psql
docker exec -it cloudsync-postgres psql -U postgres -d cloudsync_testDROP EXTENSION cloudsync CASCADE;
CREATE EXTENSION cloudsync;
SELECT cloudsync_version();Start with the admin profile:
docker-compose --profile admin up -dAccess pgAdmin at http://localhost:5050:
- Email:
admin@cloudsync.local - Password:
admin
Use this when you want breakpoints in the extension code.
The dev container uses docker/postgresql/Dockerfile.debug and docker/postgresql/docker-compose.debug.yml, which build the extension with debug symbols.
Required VS Code extensions:
ms-vscode-remote.remote-containers(Dev Containers)ms-vscode.cpptools(C/C++ debugging)
-
Open the dev container
VS Code -> Command Palette ->Dev Containers: Reopen in Container -
Connect with
psql(inside the dev container)
psql -U postgres -d cloudsync_test- Enable the extension if needed
CREATE EXTENSION IF NOT EXISTS cloudsync;- Get the backend PID (inside
psql)
SELECT pg_backend_pid();-
Attach the debugger (VS Code dev container window)
Run and Debug ->Attach to Postgres (gdb)-> pick the PID from step 4 -> Continue -
Trigger your breakpoint
Run the SQL that exercises the code path. Ifpsqlblocks, the backend is paused at a breakpoint; continue in the debugger.
Use this when you're running supabase start and want CloudSync inside the local stack.
The Supabase CLI uses a bundled PostgreSQL image (for example,
public.ecr.aws/supabase/postgres:17.6.1.071). Build a matching image that
includes CloudSync, then tag it with the same name so the CLI reuses it. This
keeps your local Supabase stack intact (auth, realtime, storage, etc.) while
enabling the extension in the CLI-managed Postgres container.
- Supabase CLI installed (
supabase startworks) - Docker running
-
Initialize a Supabase project (use a separate workdir to keep generated files out of the repo):
mkdir -p ~/supabase-local supabase init --workdir ~/supabase-local
-
Start Supabase once so the CLI pulls the Postgres image:
supabase start --workdir ~/supabase-local -
Build and tag a CloudSync image using the same tag as the running CLI stack:
make postgres-supabase-build
This auto-detects the running
supabase_dbimage tag and rebuilds it with CloudSync installed. If you need to override the full image tag, setSUPABASE_CLI_IMAGE=public.ecr.aws/supabase/postgres:<tag>. Example:SUPABASE_CLI_IMAGE=public.ecr.aws/supabase/postgres:17.6.1.071 make postgres-supabase-build
You can also set the Supabase base image tag explicitly (defaults to
17.6.1.071). This only affects the base image used in the Dockerfile:SUPABASE_POSTGRES_TAG=17.6.1.071 make postgres-supabase-build
-
Restart the stack:
supabase stop --workdir ~/supabase-local supabase start --workdir ~/supabase-local
You can load the extension automatically from a migration, or enable it manually.
Migration-based (notes for CLI): Supabase CLI migrations run as the postgres
role, which cannot create C extensions by default. Use manual enable or grant
USAGE on language c once, then migrations will work. Note: c is an
untrusted language, so GRANT USAGE ON LANGUAGE c is only allowed for
superusers. On the CLI/local stack, the simplest approach is to enable the
extension manually as supabase_admin after supabase db reset.
If you still want a migration file, add:
~/supabase-local/supabase/migrations/00000000000000_cloudsync.sqlContents:
CREATE EXTENSION IF NOT EXISTS cloudsync;Then either:
- run
GRANT USAGE ON LANGUAGE c TO postgres;once assupabase_admin, or - skip the migration and enable the extension manually after
supabase db reset.
Manual enable (no reset required):
Connect as the Supabase superuser (C extensions require superuser or language privileges), then enable the extension:
psql postgresql://supabase_admin:postgres@127.0.0.1:54322/postgresCREATE EXTENSION cloudsync;
SELECT cloudsync_version();If you want to use the postgres role instead:
GRANT USAGE ON LANGUAGE c TO postgres;If you modify the CloudSync source code, rebuild the CLI image and restart:
make postgres-supabase-rebuild SUPABASE_WORKDIR=~/supabase-localIf Supabase Realtime fails to start with:
ERROR 42P01 (undefined_table) relation "app_schema_version" does not exist
it's caused by CloudSync's app_schema_change event trigger firing during
migrations while Realtime uses a restricted search_path. Fix it by
fully qualifying the table in the trigger function:
CREATE TABLE IF NOT EXISTS public.app_schema_version (
version BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
);
CREATE OR REPLACE FUNCTION bump_app_schema_version()
RETURNS event_trigger AS $$
BEGIN
INSERT INTO public.app_schema_version DEFAULT VALUES;
END;
$$ LANGUAGE plpgsql;
DROP EVENT TRIGGER IF EXISTS app_schema_change;
CREATE EVENT TRIGGER app_schema_change
ON ddl_command_end
EXECUTE FUNCTION bump_app_schema_version();Edit source files in src/postgresql/ or src/ (shared code).
Fast method (rebuild in running container):
make postgres-dev-rebuildOr manually:
docker exec -it cloudsync-postgres bash
cd /tmp/cloudsync
make postgres-clean && make postgres-build && make postgres-installdocker exec -it cloudsync-postgres psql -U postgres -d cloudsync_test-- Reload extension
DROP EXTENSION IF EXISTS cloudsync CASCADE;
CREATE EXTENSION cloudsync;
-- Test your changes
SELECT cloudsync_version();
SELECT cloudsync_init('test_table');If you get "could not open extension control file", the extension wasn't installed correctly:
# Check installation paths
pg_config --sharedir # Should contain cloudsync.control
pg_config --pkglibdir # Should contain cloudsync.so
# Reinstall
cd /tmp/cloudsync
make install POSTGRES=1If you encounter build errors:
# Install missing dependencies
apt-get update
apt-get install -y build-essential postgresql-server-dev-16
# Clean and rebuild
make clean
make POSTGRES=1If you can't connect to PostgreSQL:
# Check if PostgreSQL is running
docker ps | grep postgres
# Check logs
docker logs cloudsync-postgres
# Restart container
docker-compose restartYou can customize the setup using environment variables:
# PostgreSQL
export POSTGRES_PASSWORD=mypassword
export POSTGRES_DB=mydb
# Ports
export POSTGRES_PORT=5432
export PGADMIN_PORT=5050
docker-compose up -d# Stop containers
docker-compose down
# Remove volumes (deletes all data!)
docker-compose down -v
# Remove images
docker rmi sqliteai/sqlite-sync-pg:latest