Developer Tools
Migration
Preflight and run reviewed live copies, portable bundle exports, and resumable bundle imports from the Varc CLI.
Varc runs foreground, one-shot migrations between Redis-compatible targets or through a portable migration bundle. Every operation is planned before writes, uses dedicated connections, and produces a complete, incomplete, or indeterminate terminal result.
Migration planning and execution require a Pro or Team CLI Token with the one-shot migration capability.
Choose a migration mode
| Mode | Reads from | Writes to | Use it for |
|---|---|---|---|
live_copy |
Source connection | Destination | Direct foreground copy between reachable hosts. |
bundle_export |
Source connection | .varcmigrate |
Portable, offline, or staged transfer. |
bundle_import |
.varcmigrate |
Destination | Verified import with optional checkpoints. |
Definitions live under [migrations.<name>] in varc.toml. Each definition
has its own schema_version = 1 and strict mode-specific fields.
Plan, review, then run
Planning connects and performs mandatory preflight without copying keys:
export VARC_CLI_TOKEN='varc_cli_...'
varc migrate plan tenant_copy
Write a versioned, non-secret plan artifact when the review and execution need to be separated:
varc migrate plan tenant_copy \
--plan-output artifacts/tenant-copy.plan.json
varc migrate run --plan artifacts/tenant-copy.plan.json
The run verifies the plan digest, redials both endpoints, repeats preflight, and refuses changed stable endpoint evidence before writing. Plan and run each obtain their own short-lived purpose-specific grant.
run is the explicit write intent. There is no generic --yes flag. Concrete
risks such as raw bundle export and excessive clock skew use named
acknowledgements instead.
Live copy
Use live_copy when both endpoints are reachable for the full operation:
[connections.source]
url_env = "SOURCE_REDIS_URL"
[connections.destination]
url_env = "DESTINATION_REDIS_URL"
[migrations.tenant_copy]
schema_version = 1
mode = "live_copy"
source = "source"
destination = "destination"
[migrations.tenant_copy.scope]
key_type = "hash"
matcher = { kind = "prefix", encoding = "text", value = "tenant:" }
[migrations.tenant_copy.policy]
conflict = "stop"
expiration = "keep_expiry_time"
verification = "quick"
allow_compatibility_skips = false
[migrations.tenant_copy.execution]
max_keys_per_second = 5000
timeout = "2h"
[migrations.tenant_copy.output]
progress = "auto"
progress_interval = "10s"
problems_file = "artifacts/tenant-copy.problems.jsonl"
varc migrate plan tenant_copy
varc migrate run tenant_copy
The same named connection can be the source and destination only when the
selected databases differ. Cluster databases must be 0.
Export a portable bundle
Bundle export reads from one source and writes a .varcmigrate file:
[migrations.tenant_export]
schema_version = 1
mode = "bundle_export"
source = "source"
bundle_path = "artifacts/tenant.varcmigrate"
overwrite = "refuse"
estimated_uncompressed_bytes = 10737418240
minimum_free_after_export_bytes = 1073741824
source_labels = ["production", "pre-cutover"]
[migrations.tenant_export.scope]
matcher = { kind = "glob", encoding = "text", value = "tenant:*" }
[migrations.tenant_export.safety]
raw_export_acknowledged = true
[migrations.tenant_export.output]
progress = "plain"
problems_file = "artifacts/tenant-export.problems.jsonl"
varc migrate plan tenant_export
varc migrate run tenant_export
raw_export_acknowledged confirms that the portable bundle contains raw data.
The free-space reserve is checked in addition to the conservative uncompressed
size estimate. Existing bundles are refused unless overwrite is explicitly set
to replace_validated_regular_file.
Import with a checkpoint
Bundle import validates the bundle before writing to the destination:
[migrations.tenant_import]
schema_version = 1
mode = "bundle_import"
bundle_path = "artifacts/tenant.varcmigrate"
destination = "destination"
[migrations.tenant_import.policy]
conflict = "stop"
expiration = "keep_expiry_time"
verification = "quick"
allow_compatibility_skips = false
[migrations.tenant_import.execution]
max_keys_per_second = 5000
timeout = "4h"
[migrations.tenant_import.output]
progress = "auto"
progress_file = "artifacts/tenant-import.progress.jsonl"
problems_file = "artifacts/tenant-import.problems.jsonl"
checkpoint_file = "artifacts/tenant-import.checkpoint.json"
varc migrate plan tenant_import
varc migrate run tenant_import
The versioned checkpoint records completed bundle chunks for a later reviewed import attempt. It does not turn an interrupted operation into a success or authorize blind replay of an indeterminate write.
Scope and policy
A migration scope can select one key type and one matcher; when both are set, they use AND semantics. Matchers support binary-safe prefix, suffix, and glob values encoded as text, hex, or Base64, plus a UTF-8 regex:
matcher = { kind = "prefix", encoding = "text", value = "tenant:" }
matcher = { kind = "suffix", encoding = "hex", value = "00ff" }
matcher = { kind = "glob", encoding = "base64", value = "dGVuYW50Oio=" }
matcher = { kind = "regex", pattern = "^tenant:\\x00[0-9]+$" }
For live copy and import, policy controls conflicts, expiry handling, verification, and compatibility skips:
conflict = "stop","skip", or"replace";expiration = "keep_expiry_time"or"restart_captured_ttl"; andverification = "quick"or"none".
Failed writes and verification failures are always incomplete. Conflict skips
are successful only under conflict = "skip". Compatibility skips remain
incomplete unless allow_compatibility_skips is explicitly enabled.
Progress and output
Visible progress is written to stderr; the final human or JSON result remains
the only stdout output. progress = "auto" selects a one-line TTY display in
an interactive terminal and plain periodic heartbeats in CI. You can also
choose tty, plain, jsonl, or none.
varc --non-interactive --format json migrate run tenant_copy \
--progress jsonl \
--progress-file artifacts/tenant-copy.progress.jsonl
Named migrations accept operational overrides such as progress paths and
intervals, timeout, output replacement, checkpoint path, and the named safety
acknowledgements. They reject target, mode, scope, and policy flags so a
reviewed definition cannot be silently redirected. Omit the migration name and
use varc migrate --help for the equivalent ad-hoc definition flags.
Problems files are bounded and binary-safe and never contain values or DUMP payloads. Existing output files are replaced only when replacement is enabled; symlinks are refused.
Interpret the result safely
Exit code 0 means the migration completed under the selected policy. Code 5
means the aggregate result is incomplete or indeterminate. Do not blindly retry
an indeterminate write: review the terminal result, problems file, and any
import checkpoint before deciding how to continue.
Clock skew above the engine threshold must be acknowledged with
clock_skew_acknowledged = true or --acknowledge-clock-skew. This gate applies
to live copy and bundle import. Raw-data acknowledgement applies only to bundle
export.