New to Kestra?
Use blueprints to kickstart your first workflows.
Orchestrate incremental SQL Server to SQL Server sync with Kestra. Extract changed rows since a watermark and bulk-insert them with chunked, resumable JDBC batches.
Move only what changed between two SQL Server instances, and never lose track of where the last run left off. This blueprint reads a watermark from Kestra's KV Store, pulls every row updated since that point from a source table, bulk-inserts the delta into a destination instance using chunked JDBC batch operations, then advances the watermark only after the load commits. A failed run leaves the watermark untouched, so the next execution retries the exact same window instead of skipping rows or double-loading them.
read_watermark (io.kestra.plugin.core.kv.Get) reads the orders_sync_watermark key from the namespace KV Store. On the first run, errorOnMissing: false lets the flow fall through to the epoch (1900-01-01T00:00:00).extract_since_watermark (io.kestra.plugin.jdbc.sqlserver.Query) selects every row from dbo.orders where updated_at is greater than the watermark, binding it as a :watermark prepared-statement parameter, and stores the result set with fetchType: STORE.bulk_load (io.kestra.plugin.jdbc.sqlserver.Batch) reads that stored file and bulk-inserts it into dbo.orders_reporting on the destination instance, in chunks of 1,000 rows per JDBC batch call.compute_new_watermark (io.kestra.plugin.jdbc.sqlserver.Query) re-reads the maximum updated_at committed on the source for the same window, with fetchType: FETCH_ONE.advance_watermark (io.kestra.plugin.core.kv.Set) writes that value back to orders_sync_watermark, but only after bulk_load has already succeeded, so a mid-batch failure never advances the watermark past what was actually committed downstream.notify (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) confirms the row count and new watermark; the errors block posts a separate Slack alert on failure.Source and destination credentials are split across two pluginDefaults blocks (one for Query, one for Batch), so the same flow can point at two different SQL Server instances without repeating connection settings on every task.
INSERT statements.SQL Server Agent can run a stored procedure on a schedule, but it has no built-in concept of a portable watermark, no chunked bulk-insert primitive across two different instances, and no way to skip a Slack alert on success while still alerting on failure. Kestra adds all of that: the KV Store tracks state across runs, io.kestra.plugin.jdbc.sqlserver.Batch chunks the insert with automatic retry and resume behavior, and the errors block guarantees a failed run is never silent.
dbo.orders table containing an updated_at column suitable as a watermark.dbo.orders_reporting table matching the selected columns.SQLSERVER_SOURCE_URL: JDBC URL for the source instance, for example jdbc:sqlserver://source-host:1433;trustServerCertificate=true.SQLSERVER_SOURCE_USERNAME / SQLSERVER_SOURCE_PASSWORD: credentials for the source instance.SQLSERVER_DEST_URL: JDBC URL for the destination instance.SQLSERVER_DEST_USERNAME / SQLSERVER_DEST_PASSWORD: credentials for the destination instance.SLACK_WEBHOOK_URL: Slack incoming webhook URL.dbo.orders table and destination dbo.orders_reporting table exist with matching columns.Schedule trigger.orders_sync_watermark key after each run to confirm it advances.table for an explicit sql INSERT statement on Batch if the source and destination column order differs.Schedule trigger with a Flow trigger so the sync fires the moment an upstream load finishes.If check after bulk_load to branch on an unexpectedly large or small batch before advancing the watermark.bulk_load for a full cross-database ETL pattern.