What this is
RustFS server now ships On-Demand Migration: a bucket can name an external S3-compatible source bucket, a GET that misses locally is served from that source and stored locally, and a background backfill job pulls the rest. It is the RustFS equivalent of Cloudflare R2's Sippy (wrangler r2 bucket sippy enable) or Tigris buckets set-migration.
The server side is complete and merged (tracking plan rustfs/backlog#2147; operations guide docs/operations/on-demand-migration.md in rustfs/rustfs). This issue asks rc for the command surface. The wire contract is pinned by golden fixtures in crates/madmin/fixtures/on_demand_migration/ — parse tests should read those files (vendored with the source commit noted) rather than hand-written literals.
Proposed commands
rc admin bucket migration set <alias>/<bucket> --provider s3|aws|minio|rustfs|r2|gcs --endpoint URL --region R --source-bucket B [--prefix P] [--source-prefix SP] [--access-key AK --secret-key SK | --public] [--path-style auto|path|virtual] [--skip-tls-verify] [--ca-cert FILE] [--head proxy|local_only] [--range-get serve_and_backfill|serve_only] [--source-error propagate|not_found] [--no-preserve-etag] [--copy-tags] [--no-events] [--inline-max-bytes N] [--max-concurrent-pulls N] [--dry-run]
rc admin bucket migration get <alias>/<bucket> [--json]
rc admin bucket migration rm <alias>/<bucket>
rc admin bucket migration status <alias>/<bucket> [--json] [--watch]
rc admin bucket migration backfill start <alias>/<bucket> [--prefix P] [--skip-existing always|etag_or_size] [--dry-run]
rc admin bucket migration backfill cancel <alias>/<bucket>
rc admin bucket migration backfill status <alias>/<bucket> [--json] [--watch]
--dry-run on set maps to PUT ...?dry-run=true, which validates the config and probes the source without saving.
Details that matter
- Never take the secret from argv when avoidable. Read
--secret-key from RC_ODM_SECRET_KEY or a hidden prompt when the flag is absent, so it stays out of shell history.
- Human-readable output.
get prints the redacted config as a table (the server returns secret_key: "REDACTED"). status prints the source-hit ratio, migrated bytes, in-flight and queued pulls, breaker state and the last source error; served_by_source_ratio is deliberately null on the server and must render as an em dash, never as zero. backfill status --watch refreshes a progress line every couple of seconds.
- Forward compatibility. Every field optional with a sane default, so an older server that omits fields does not break parsing; a 404 on the route family means the server predates the feature, so print "server does not support on-demand migration" rather than a stack trace.
- Exit codes. Validation failure 2, source unreachable 3, licence rejection 4, backfill conflict (409) 5.
- Editing an existing configuration replaces it wholesale, so
set must require the secret again rather than sending back the redaction placeholder.
Admin API
| Method |
Path |
PUT / GET / DELETE |
/rustfs/admin/v3/on-demand-migration/{bucket} |
GET |
/rustfs/admin/v3/on-demand-migration/{bucket}/status |
POST |
/rustfs/admin/v3/on-demand-migration/{bucket}/backfill?op=start|cancel |
GET |
/rustfs/admin/v3/on-demand-migration/{bucket}/backfill |
Actions: admin:SetBucketOnDemandMigration for writes, admin:GetBucketOnDemandMigration for reads.
References
- Tracking plan: rustfs/backlog#2147
- This task: rustfs/backlog#2162
- Operations guide:
docs/operations/on-demand-migration.md in rustfs/rustfs
- Wire fixtures:
crates/madmin/fixtures/on_demand_migration/*.json in rustfs/rustfs
What this is
RustFS server now ships On-Demand Migration: a bucket can name an external S3-compatible source bucket, a GET that misses locally is served from that source and stored locally, and a background backfill job pulls the rest. It is the RustFS equivalent of Cloudflare R2's Sippy (
wrangler r2 bucket sippy enable) or Tigrisbuckets set-migration.The server side is complete and merged (tracking plan rustfs/backlog#2147; operations guide
docs/operations/on-demand-migration.mdin rustfs/rustfs). This issue asksrcfor the command surface. The wire contract is pinned by golden fixtures incrates/madmin/fixtures/on_demand_migration/— parse tests should read those files (vendored with the source commit noted) rather than hand-written literals.Proposed commands
--dry-runonsetmaps toPUT ...?dry-run=true, which validates the config and probes the source without saving.Details that matter
--secret-keyfromRC_ODM_SECRET_KEYor a hidden prompt when the flag is absent, so it stays out of shell history.getprints the redacted config as a table (the server returnssecret_key: "REDACTED").statusprints the source-hit ratio, migrated bytes, in-flight and queued pulls, breaker state and the last source error;served_by_source_ratiois deliberatelynullon the server and must render as an em dash, never as zero.backfill status --watchrefreshes a progress line every couple of seconds.setmust require the secret again rather than sending back the redaction placeholder.Admin API
PUT/GET/DELETE/rustfs/admin/v3/on-demand-migration/{bucket}GET/rustfs/admin/v3/on-demand-migration/{bucket}/statusPOST/rustfs/admin/v3/on-demand-migration/{bucket}/backfill?op=start|cancelGET/rustfs/admin/v3/on-demand-migration/{bucket}/backfillActions:
admin:SetBucketOnDemandMigrationfor writes,admin:GetBucketOnDemandMigrationfor reads.References
docs/operations/on-demand-migration.mdin rustfs/rustfscrates/madmin/fixtures/on_demand_migration/*.jsonin rustfs/rustfs