From be677f50a5838d5202f7ad473310a496499603a7 Mon Sep 17 00:00:00 2001 From: ahmed-lotfy-dev Date: Fri, 15 May 2026 18:54:52 +0300 Subject: [PATCH 1/4] feat: add powersync blueprint template --- blueprints/powersync/docker-compose.yml | 15 +++++++++++++++ blueprints/powersync/powersync.png | Bin 0 -> 1533 bytes blueprints/powersync/template.toml | 20 ++++++++++++++++++++ meta.json | 19 +++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 blueprints/powersync/docker-compose.yml create mode 100644 blueprints/powersync/powersync.png create mode 100755 blueprints/powersync/template.toml diff --git a/blueprints/powersync/docker-compose.yml b/blueprints/powersync/docker-compose.yml new file mode 100644 index 00000000..02fa6a02 --- /dev/null +++ b/blueprints/powersync/docker-compose.yml @@ -0,0 +1,15 @@ +services: + powersync: + image: journeyapps/powersync-service:1.21.0 + restart: unless-stopped + environment: + POWERSYNC_CONFIG_B64: ${POWERSYNC_CONFIG_B64} + POWERSYNC_SYNC_CONFIG_B64: ${POWERSYNC_SYNC_CONFIG_B64} + PS_DATA_SOURCE_URI: ${PS_DATA_SOURCE_URI} + PS_PORT: ${PS_PORT:-8080} + PS_JWKS_URL: ${PS_JWKS_URL:-} + PS_ADMIN_TOKEN: ${PS_ADMIN_TOKEN} + NODE_OPTIONS: ${NODE_OPTIONS} + ports: + - "${PS_PORT:-8080}:${PS_PORT:-8080}" + command: ["start", "-r", "unified"] diff --git a/blueprints/powersync/powersync.png b/blueprints/powersync/powersync.png new file mode 100644 index 0000000000000000000000000000000000000000..779f02c4c11c782e07ec3dd2b42c824130939319 GIT binary patch literal 1533 zcmeAS@N?(olHy`uVBq!ia0y~yU|a&i985rwk9xtZqvMyyau)V?2bj{Xf1ktlL&YupztOQD>>sYzo|R+xz_O89=m?*OfVG0- zn}$&irD2d*`NsSCF8gn~b#F^^Hmk>+T>YEl{2r_6vJ(`_9GY%0awf5>bPPYvhBI;p zsx7bAeq-6udV2V$ro@@H4>s@Z%l+1(J@>U;^`WnSll~a>d%lT&h2uVE28RFt7fliWH1mrYAFx Date: Fri, 15 May 2026 19:01:17 +0300 Subject: [PATCH 2/4] fix: process meta.json (sort + dedupe) --- meta.json | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/meta.json b/meta.json index a6e2c381..69d879cd 100644 --- a/meta.json +++ b/meta.json @@ -5161,25 +5161,6 @@ "api" ] }, - { - "id": "powersync", - "name": "PowerSync", - "version": "1.21.0", - "description": "PowerSync is a sync engine for local-first apps. Syncs PostgreSQL data to mobile devices and browsers with real-time replication.", - "logo": "powersync.png", - "links": { - "github": "https://github.com/powersync-ja/powersync-service", - "website": "https://www.powersync.com/", - "docs": "https://docs.powersync.com/" - }, - "tags": [ - "sync", - "database", - "local-first", - "postgresql", - "realtime" - ] - }, { "id": "poke", "name": "Poke", @@ -5291,6 +5272,25 @@ "publishing" ] }, + { + "id": "powersync", + "name": "PowerSync", + "version": "1.21.0", + "description": "PowerSync is a sync engine for local-first apps. Syncs PostgreSQL data to mobile devices and browsers with real-time replication.", + "logo": "powersync.png", + "links": { + "github": "https://github.com/powersync-ja/powersync-service", + "website": "https://www.powersync.com/", + "docs": "https://docs.powersync.com/" + }, + "tags": [ + "sync", + "database", + "local-first", + "postgresql", + "realtime" + ] + }, { "id": "pre0.22.5-supabase", "name": "SupaBase", From a9a40ec69a72d46d80491a4648896863690132e9 Mon Sep 17 00:00:00 2001 From: ahmed-lotfy-dev Date: Fri, 15 May 2026 20:02:21 +0300 Subject: [PATCH 3/4] fix: update powersync base64 config to match working deployment + add env comments --- blueprints/powersync/template.toml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/blueprints/powersync/template.toml b/blueprints/powersync/template.toml index da6b9686..e574b059 100755 --- a/blueprints/powersync/template.toml +++ b/blueprints/powersync/template.toml @@ -1,8 +1,10 @@ [variables] main_domain = "${domain}" +# Your PostgreSQL connection string. PowerSync connects directly to replicate + sync. +# Must have logical replication enabled (wal_level=logical). data_source_uri = "postgresql://user:password@host:5432/db?sslmode=require" +# Auto-generated admin token for API access. admin_token = "${password:32}" -jwks_url = "" [config] [[config.domains]] @@ -11,10 +13,15 @@ port = 8080 host = "${main_domain}" [config.env] +# PowerSync listens on port 8080 internally. Dokproxy/Traefik handles HTTPS. PS_PORT = "8080" +# Source + bucket storage URI. PowerSync uses PostgreSQL for both. PS_DATA_SOURCE_URI = "${data_source_uri}" +# Secret token for the admin API (used in Authorization header). PS_ADMIN_TOKEN = "${admin_token}" -PS_JWKS_URL = "${jwks_url}" +# NODE_OPTIONS --max-old-space-size=1000 limits memory to ~1GB NODE_OPTIONS = "--max-old-space-size=1000" -POWERSYNC_CONFIG_B64 = "dGVsZW1ldHJ5OgogIGRpc2FibGVfdGVsZW1ldHJ5X3NoYXJpbmc6IHRydWUKcmVwbGljYXRpb246CiAgY29ubmVjdGlvbnM6CiAgICAtIHR5cGU6IHBvc3RncmVzcWwKICAgICAgdXJpOiAhZW52IFBTX0RBVEFfU09VUkNFX1VSSQogICAgICBzc2xtb2RlOiByZXF1aXJlCnN0b3JhZ2U6CiAgdHlwZTogcG9zdGdyZXNxbAogIHVyaTogIWVudiBQU19EQVRBX1NPVVJDRV9VUkkKcG9ydDogIWVudiBQU19QT1JUCnN5bmNfY29uZmlnOgogIHBhdGg6IHN5bmMtY29uZmlnLnlhbWwKY2xpZW50X2F1dGg6CiAgandrc191cmk6ICFlbnYgUFNfSldLU19VUkwKICBhdWRpZW5jZTogWyJwb3dlcnN5bmMiXQphcGk6CiAgdG9rZW5zOgogICAgLSAhZW52IFBTX0FETUlOX1RPS0VOCnN5c3RlbToKICBsb2dnaW5nOgogICAgbGV2ZWw6IGluZm8KICAgIGZvcm1hdDogdGV4dAo=" -POWERSYNC_SYNC_CONFIG_B64 = "Y29uZmlnOgogIGVkaXRpb246IDMKc3RyZWFtczoKICBnbG9iYWw6CiAgICBhdXRvX3N1YnNjcmliZTogdHJ1ZQogICAgcXVlcmllczoKICAgICAgLSBTRUxFQ1QgKiBGUk9NIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMgV0hFUkUgdGFibGVfc2NoZW1hID0gJ3B1YmxpYycK" +# Base64-encoded PowerSync service config. +# Contains replication, storage, auth, and sync rules — all via !env PS_* vars. +# Decode with: echo '' | base64 -d +POWERSYNC_CONFIG_B64 = "dGVsZW1ldHJ5OgogIGRpc2FibGVfdGVsZW1ldHJ5X3NoYXJpbmc6IHRydWUKcmVwbGljYXRpb246CiAgY29ubmVjdGlvbnM6CiAgICAtIHR5cGU6IHBvc3RncmVzcWwKICAgICAgdXJpOiAhZW52IFBTX0RBVEFfU09VUkNFX1VSSQogICAgICBzc2xtb2RlOiBkaXNhYmxlCnN0b3JhZ2U6CiAgdHlwZTogcG9zdGdyZXNxbAogIHVyaTogIWVudiBQU19EQVRBX1NPVVJDRV9VUkkKcG9ydDogIWVudiBQU19QT1JUCnN5bmNfY29uZmlnOgogIGNvbnRlbnQ6IHwKICAgIGNvbmZpZzoKICAgICAgZWRpdGlvbjogMwogICAgc3RyZWFtczoKICAgICAgZ2xvYmFsOgogICAgICAgIGF1dG9fc3Vic2NyaWJlOiB0cnVlCiAgICAgICAgcXVlcmllczoKICAgICAgICAgIC0gU0VMRUNUICogRlJPTSBpbmZvcm1hdGlvbl9zY2hlbWEudGFibGVzIFdIRVJFIHRhYmxlX3NjaGVtYSA9ICdwdWJsaWMnCmNsaWVudF9hdXRoOgogIGF1ZGllbmNlOiBbInBvd2Vyc3luYyJdCmFwaToKICB0b2tlbnM6CiAgICAtICFlbnYgUFNfQURNSU5fVE9LRU4Kc3lzdGVtOgogIGxvZ2dpbmc6CiAgICBsZXZlbDogaW5mbwogICAgZm9ybWF0OiB0ZXh0Cg==" From 848013040101c82dd42f40e9996e3a76ad65a0dd Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Wed, 8 Jul 2026 02:05:00 -0600 Subject: [PATCH 4/4] fix: bundle Postgres so the service can actually start The template pointed PS_DATA_SOURCE_URI at a placeholder connection string (postgresql://user:password@host:5432/db), so the PowerSync service could never connect and crash-looped, leaving the domain returning Traefik 404s. Changes: - Add a bundled Postgres 17 service with wal_level=logical, a healthcheck, and a persistent volume; powersync waits for it to be healthy before starting. - Replace the base64-encoded config env var with a readable powersync.yaml mounted via [[config.mounts]]. Replication reads from the powersync database and bucket storage uses a separate powersync_storage database (Postgres storage requires a database distinct from the replication source). - Add an init SQL mount that creates the storage database, a demo "lists" table synced by the default sync rules, and the powersync publication required for logical replication. - Replace the unsupported sync_config block with standard sync_rules, and add an HS256 dev key so client auth works out of the box. - Remove host port mapping (Traefik routes to the container port) and point the template domain at /probes/liveness since PowerSync has no route on "/". Co-Authored-By: Claude Fable 5 --- blueprints/powersync/docker-compose.yml | 36 +++++++--- blueprints/powersync/template.toml | 93 ++++++++++++++++++++----- 2 files changed, 104 insertions(+), 25 deletions(-) diff --git a/blueprints/powersync/docker-compose.yml b/blueprints/powersync/docker-compose.yml index 02fa6a02..1440c8f9 100644 --- a/blueprints/powersync/docker-compose.yml +++ b/blueprints/powersync/docker-compose.yml @@ -1,15 +1,33 @@ services: + postgres: + image: postgres:17-alpine + restart: unless-stopped + command: ["postgres", "-c", "wal_level=logical"] + environment: + POSTGRES_USER: powersync + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: powersync + volumes: + - postgres-data:/var/lib/postgresql/data + - ../files/init-powersync.sql:/docker-entrypoint-initdb.d/init-powersync.sql:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U powersync -d powersync"] + interval: 5s + timeout: 5s + retries: 12 + powersync: image: journeyapps/powersync-service:1.21.0 restart: unless-stopped + depends_on: + postgres: + condition: service_healthy environment: - POWERSYNC_CONFIG_B64: ${POWERSYNC_CONFIG_B64} - POWERSYNC_SYNC_CONFIG_B64: ${POWERSYNC_SYNC_CONFIG_B64} - PS_DATA_SOURCE_URI: ${PS_DATA_SOURCE_URI} - PS_PORT: ${PS_PORT:-8080} - PS_JWKS_URL: ${PS_JWKS_URL:-} - PS_ADMIN_TOKEN: ${PS_ADMIN_TOKEN} - NODE_OPTIONS: ${NODE_OPTIONS} - ports: - - "${PS_PORT:-8080}:${PS_PORT:-8080}" + POWERSYNC_CONFIG_PATH: /config/powersync.yaml + NODE_OPTIONS: --max-old-space-size=1000 + volumes: + - ../files/powersync.yaml:/config/powersync.yaml:ro command: ["start", "-r", "unified"] + +volumes: + postgres-data: diff --git a/blueprints/powersync/template.toml b/blueprints/powersync/template.toml index e574b059..a702a17a 100755 --- a/blueprints/powersync/template.toml +++ b/blueprints/powersync/template.toml @@ -1,27 +1,88 @@ [variables] main_domain = "${domain}" -# Your PostgreSQL connection string. PowerSync connects directly to replicate + sync. -# Must have logical replication enabled (wal_level=logical). -data_source_uri = "postgresql://user:password@host:5432/db?sslmode=require" -# Auto-generated admin token for API access. +# Password for the bundled PostgreSQL (used as replication source and bucket storage). +postgres_password = "${password:32}" +# Auto-generated token for the PowerSync API (used in Authorization header). admin_token = "${password:32}" +# HS256 secret used to sign development JWTs for clients (base64url encoded). +jwks_key = "${password:48}" [config] [[config.domains]] serviceName = "powersync" port = 8080 host = "${main_domain}" +# PowerSync has no route on "/"; the liveness probe returns 200. +path = "/probes/liveness" [config.env] -# PowerSync listens on port 8080 internally. Dokproxy/Traefik handles HTTPS. -PS_PORT = "8080" -# Source + bucket storage URI. PowerSync uses PostgreSQL for both. -PS_DATA_SOURCE_URI = "${data_source_uri}" -# Secret token for the admin API (used in Authorization header). -PS_ADMIN_TOKEN = "${admin_token}" -# NODE_OPTIONS --max-old-space-size=1000 limits memory to ~1GB -NODE_OPTIONS = "--max-old-space-size=1000" -# Base64-encoded PowerSync service config. -# Contains replication, storage, auth, and sync rules — all via !env PS_* vars. -# Decode with: echo '' | base64 -d -POWERSYNC_CONFIG_B64 = "dGVsZW1ldHJ5OgogIGRpc2FibGVfdGVsZW1ldHJ5X3NoYXJpbmc6IHRydWUKcmVwbGljYXRpb246CiAgY29ubmVjdGlvbnM6CiAgICAtIHR5cGU6IHBvc3RncmVzcWwKICAgICAgdXJpOiAhZW52IFBTX0RBVEFfU09VUkNFX1VSSQogICAgICBzc2xtb2RlOiBkaXNhYmxlCnN0b3JhZ2U6CiAgdHlwZTogcG9zdGdyZXNxbAogIHVyaTogIWVudiBQU19EQVRBX1NPVVJDRV9VUkkKcG9ydDogIWVudiBQU19QT1JUCnN5bmNfY29uZmlnOgogIGNvbnRlbnQ6IHwKICAgIGNvbmZpZzoKICAgICAgZWRpdGlvbjogMwogICAgc3RyZWFtczoKICAgICAgZ2xvYmFsOgogICAgICAgIGF1dG9fc3Vic2NyaWJlOiB0cnVlCiAgICAgICAgcXVlcmllczoKICAgICAgICAgIC0gU0VMRUNUICogRlJPTSBpbmZvcm1hdGlvbl9zY2hlbWEudGFibGVzIFdIRVJFIHRhYmxlX3NjaGVtYSA9ICdwdWJsaWMnCmNsaWVudF9hdXRoOgogIGF1ZGllbmNlOiBbInBvd2Vyc3luYyJdCmFwaToKICB0b2tlbnM6CiAgICAtICFlbnYgUFNfQURNSU5fVE9LRU4Kc3lzdGVtOgogIGxvZ2dpbmc6CiAgICBsZXZlbDogaW5mbwogICAgZm9ybWF0OiB0ZXh0Cg==" +POSTGRES_PASSWORD = "${postgres_password}" + +# PowerSync service configuration. +# Docs: https://docs.powersync.com/self-hosting/installation/powersync-service-setup +[[config.mounts]] +filePath = "powersync.yaml" +content = """ +telemetry: + disable_telemetry_sharing: true + +# Replication source: the bundled Postgres (wal_level=logical). +# Point this at your own database to sync real data. +replication: + connections: + - type: postgresql + uri: postgresql://powersync:${postgres_password}@postgres:5432/powersync + sslmode: disable + +# Bucket storage. Must be a different database than the replication source. +storage: + type: postgresql + uri: postgresql://powersync:${postgres_password}@postgres:5432/powersync_storage + sslmode: disable + +port: 8080 + +# Sync rules: syncs the demo "lists" table to all clients. +# Edit these for your own schema: https://docs.powersync.com/usage/sync-rules +sync_rules: + content: | + bucket_definitions: + global: + data: + - SELECT * FROM lists + +client_auth: + # HS256 key for signing development tokens. + # See https://docs.powersync.com/installation/authentication-setup/custom + jwks: + keys: + - kty: oct + alg: HS256 + kid: powersync-dev + k: ${jwks_key} + audience: ["powersync"] + +api: + tokens: + - ${admin_token} +""" + +# Runs once on the first Postgres startup (empty data volume). +[[config.mounts]] +filePath = "init-powersync.sql" +content = """ +-- Separate database for PowerSync bucket storage. +CREATE DATABASE powersync_storage OWNER powersync; + +-- Demo table synced by the default sync rules. +CREATE TABLE lists ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + name text NOT NULL, + created_at timestamptz NOT NULL DEFAULT now() +); + +INSERT INTO lists (name) VALUES ('Welcome to PowerSync'); + +-- Publication used by PowerSync logical replication. +CREATE PUBLICATION powersync FOR ALL TABLES; +"""