Syncle

Troubleshooting

The failures people actually hit, and what each one means. Most of them are a database setting rather than a bug in Syncle — CDC in particular needs the source server configured for it, and no tool can turn that on from the outside.

syncle up exits without starting

Two known causes. If the message mentions resolving a reference, you are on an install older than 1.2.0, where the installer asked for the image by its release tag (v1.2.0) while images publish without the prefix (1.2.0). Reinstalling picks up the fixed script.

If the machine is offline, upgrade to 1.2.0 or later. Before it, a failed image pull aborted the whole start, so a host with the images already cached could not run Syncle at all; the pull is now best-effort.

Otherwise it is usually the port. Syncle publishes one, 3002, and refuses to start if something already holds it:

lsof -i :3002

The setup form wants a token

syncle up normally reads the first-run token off the server and opens the interface with it already accepted. Opening Syncle from a different device skips that, because the token is only readable by something with access to the container. Print it and paste it in:

syncle logs api

If the account already exists, there is no token to find — the API deletes it the moment setup succeeds, and clears it at boot when an account is present. Use the login form instead.

A CDC bridge never delivers anything

Almost always the source server is not configured for change data capture. The builder checks this before it lets you save, and names the setting that is missing — if you skipped past that, re-open the bridge and look at the trigger step. What each engine needs:

PostgreSQL

wal_level=logical, set in postgresql.conf or your provider's parameter group, followed by a server restart. This is the one prerequisite that cannot be automated, because it needs the restart.

MySQL and MariaDB

Row-based binary logging, in my.cnf: log_bin=ON, binlog_format=ROW, binlog_row_image=FULL, and a unique server_id. It needs a server restart. On managed MySQL — RDS, Aurora, Cloud SQL — set these in the parameter group and reboot.

MongoDB

Change streams require a replica set. A single-node replica set is fine for development: start mongod with --replSet rs0 and run rs.initiate() once. Atlas already satisfies this.

Redis

Keyspace notifications, which Syncle tries to enable itself on start:

CONFIG SET notify-keyspace-events EA

Managed Redis usually refuses that and wants it enabled in the provider console instead. It can also be set permanently in redis.conf.

A watch bridge delivers nothing

A new watch bridge starts from now by default, so it ignores everything already in the table and only delivers rows that appear after it started. If you wanted the existing rows too, either set it to start from the beginning, or run a replay job once to backfill and leave the watch running for what follows.

If it delivers nothing even for new rows, check the cursor matches the table. A timestamp cursor on a column the application does not update will never advance; a table whose primary keys are UUIDs needs the primary-key diff strategy rather than an incrementing id. A cursor column holding future timestamps parks the bridge until real time catches up.

Deletes are not crossing the bridge

Expected on a watch bridge. Watch polls for rows that exist, so it sees inserts, and updates when the cursor is a timestamp, but a deleted row is simply absent from the next poll and indistinguishable from one that was never there. Deletes need a CDC trigger, which reads them from the change log.

A connection will not test

An error beginning SSH: comes from the tunnel, not the database — the jump host refused the key, the user, or the forward. Check the SSH credentials on their own before looking at the database.

Without a tunnel, the usual causes are the database not listening on an interface Syncle can reach, or TLS. Syncle connects out to your database, so the database has to accept a connection from the Docker host. On the same machine, that is generally host.docker.internal rather than localhost, which inside a container means the container.

Rows are failing rather than syncing

Click a failed delivery on the timeline: it shows the row that was sent and the error that came back. Failed rows can be retried in place without rerunning the whole job.

A bridge that fails everything usually has a destination mismatch — key columns that are not unique in the target, or a type the target will not accept. A bridge that fails intermittently against an HTTP endpoint is usually being rate limited; raise the minimum delay between requests, or lower the batch size, in the bridge's delivery settings.

Still stuck

syncle logs api carries the server side of anything the interface could not explain. If it looks like a bug, open an issue with the engine, the trigger type, and that log; if you are unsure whether it is a bug, Discussions is the better place to start.