sluice

Getting started

Install sluice, point it at a source and target, and run your first migration and continuous sync.

Install

sluice is a single static binary with no daemon and no SaaS dependency. Install with the Go toolchain:

go install sluicesync.dev/sluice/cmd/sluice@latest

Or run the official container image (multi-arch, distroless):

docker run --rm ghcr.io/sluicesync/sluice:latest --version

Pre-built Linux / macOS / Windows binaries are attached to every tagged release. Verify the install:

sluice --version
sluice engines      # list the database engines built into this binary

Prerequisites

Connecting to your databases

Source and target are passed as DSNs (connection strings). The driver is named separately with --source-driver / --target-driver.

EngineDSN format
mysqluser:pass@tcp(host:3306)/dbname
postgrespostgres://user:pass@host:5432/dbname?sslmode=require

DSNs often contain credentials, so you can supply them via environment variables instead of flags:

export SLUICE_SOURCE='root:rootpw@tcp(localhost:3306)/app'
export SLUICE_TARGET='postgres://postgres:pgpw@localhost:5432/app?sslmode=disable'

See Configuration for the full set of environment variables and the optional YAML config file.

Your first migration

A one-shot migration translates the source schema, creates the target tables, bulk-copies rows, then builds indexes and constraints. Always do a dry run first — it reads the source schema and prints the plan without touching the target:

sluice migrate \
    --source-driver mysql    --source 'root:rootpw@tcp(localhost:3306)/app' \
    --target-driver postgres --target 'postgres://postgres:pgpw@localhost:5432/app?sslmode=disable' \
    --dry-run

When the plan looks right, drop --dry-run to apply it. If a migration is interrupted, re-run with --resume — state is checkpointed per table on the target, so it picks up where it left off:

sluice migrate --source-driver mysql --source ... --target-driver postgres --target ... --resume
Cold-start safety. sluice refuses to bulk-copy into a non-empty target by default (an INSERT into a populated table would collide on the primary key). Use --resume to continue a prior run, or read the migrate reference for the recovery flags.

Your first continuous sync

Continuous sync captures a consistent snapshot, bulk-copies it, then streams ongoing changes. Streams are identified by a --stream-id so they can resume after a restart:

sluice sync start \
    --source-driver mysql    --source 'root:rootpw@tcp(localhost:3306)/app' \
    --target-driver postgres --target 'postgres://postgres:pgpw@localhost:5432/app?sslmode=disable' \
    --stream-id app-prod

From another shell, check freshness or status, and stop the stream cleanly when you're done:

sluice sync status --stream-id app-prod --target-driver postgres --target ...
sluice sync health --stream-id app-prod --target-driver postgres --target ...   # cron-friendly exit code
sluice sync stop   --stream-id app-prod --target-driver postgres --target ...   # drains in-flight changes, then exits

Verify the copy

After a migration or once a stream has caught up, compare source and target:

sluice verify \
    --source-driver mysql    --source ... \
    --target-driver postgres --target ...

verify compares row counts by default and can escalate to per-row hashing — see the verify reference.

Next steps