Webhook icon
Schedule icon
If icon
Fail icon
Commands icon
Docker icon
Loop icon
Sequential icon
Set icon
SlackIncomingWebhook icon
Pause icon
LoopUntil icon

Catch MinIO Replication Drift Before Objects Go Missing on the Target

Check MinIO bucket replication health and spot-check objects on the target. Alerts once per change to Slack and runs a resync only after approval.

Categories
DataInfrastructure

Bucket replication in MinIO fails quietly. A target that is down, a rule that was disabled during a deployment, or objects removed on the target leave the source looking healthy while the copy is incomplete. Nothing in the source tells you the target is short. This blueprint compares the two deployments directly, bucket by bucket.

It runs locally against any two MinIO deployments with versioning and bucket replication. Each run reads the replication rule and the replication status with mc, lists the objects on both sides, and compares them by key, size and ETag. Slack receives a message only when the set of problems changes. When objects are missing on the target and the rule is enabled, the run pauses and asks on-call whether to start a resync. It waits for the resync to finish and checks the buckets again.

How it works

  1. validate_inputs and validate_bucket_names (If) fail before any call when the inputs are empty, negative or not valid bucket names.
  2. check_source and check_target (Commands, minio/mc) check that both deployments answer, with retries.
  3. audit_buckets (Loop) runs one Sequential per bucket, each with allowFailure:
    • replication_rules, replication_status, replication_backlog read the rule, the link state and every unreplicated object version with its last attempt.
    • list_objects lists the current objects on both sides.
    • report (Commands, Python standard library) spot-checks the objects and merges everything into one result per bucket.
  4. decide compares the problems with the state in the KV store and decides whether to alert and whether to offer a resync.
  5. save_state (kv.Set) stores the signature of the run.
  6. notify_change (SlackIncomingWebhook) sends one message per change.
  7. if_resync_offered shows a Slack link and a Pause with a SELECT (Start resync or Decline). The pause is 2 hours and the default is no change.
  8. resync_buckets (Loop) rechecks the rule, starts mc replicate resync start for the ARN, waits with LoopUntil until the resync is Completed or Failed and nothing is pending, then runs the spot check again.

What you get

  • One Slack message per change, naming each bucket, the failed and pending counts, the last failure, and the missing keys.
  • Flow outputs: buckets (JSON), with one entry per bucket and the decision.

Error handling

  • Unreachable source or target: check_source and check_target retry, then the errors block posts the failed task name and the cause.
  • One broken bucket (missing bucket, no permission) does not stop the others. Its Sequential is isolated with allowFailure, and the report shows it as an error.
  • A bucket without a replication rule is a finding, not a failure.
  • Approval comes late: the rule is checked again before the resync starts. If the check refuses it, the run ends as WARNING and the Slack report says no resync was done.
  • A resync that never completes fails its bucket after 15 minutes instead of hanging.
  • Nothing changes without an answer: the pause resumes with no decision.
  • The state is written before any message, so a retried run does not alert twice for the same problems.

What this blueprint teaches about Kestra

  • Loop with Sequential and allowFailure to isolate per-item failures.
  • LoopUntil with checkFrequency and failOnMaxReached for a wait with a deadline.
  • Pause with onResume inputs, behavior: RESUME, and reading the answer with ??.
  • KV store as state for "alert once per change", with kv() and kv.Set.
  • Commands with the Docker task runner, networkMode: host, and outputFiles passed between tasks.
  • If and Fail for validation before any side effect.

Prerequisites

  • Two MinIO deployments with versioning enabled on every bucket, and a bucket replication rule from source to target.
  • Docker on the Kestra worker, so the minio/mc and python task images can run. The worker needs network access to both deployments.
  • A Slack incoming webhook.

Secrets

  • MINIO_SOURCE_ENDPOINT: URL of the source deployment, for example http://minio-src.internal:9000.
  • MINIO_SOURCE_ACCESS_KEY and MINIO_SOURCE_SECRET_KEY: credentials with list, read and replication-admin rights on the source.
  • MINIO_TARGET_ENDPOINT: URL of the target deployment.
  • MINIO_TARGET_ACCESS_KEY and MINIO_TARGET_SECRET_KEY: credentials with list rights on the target.
  • SLACK_WEBHOOK_URL: the incoming webhook for alerts and approval requests.
  • MINIO_REPLICATION_WEBHOOK_KEY: a long random string, the key in the webhook URL ($KEY in the trigger example).

Inputs

  • buckets (ARRAY of STRING, default ["orders"]): buckets to audit.
  • sample_size (INT, default 200): objects to spot-check per bucket; 0 checks every object.
  • backlog_warn_objects (INT, default 100): pending objects above which the backlog is reported.
  • kestra_url (STRING, default http://localhost:8080): base URL for the approval link.

Outputs

  • buckets (JSON): the decision and the per-bucket results of the run. Empty when the run failed before the decision.

Quick start

  1. Create the secrets above in Kestra.
  2. Create a versioned bucket on both deployments, and add a replication rule from the source to the target with mc replicate add.
  3. Run the flow with the defaults. The first run is silent when the buckets are healthy. Call the webhook to run it on demand, or enable the schedule.

How to read the result

  • missing means the key is on the source and not on the target. mismatched means the key is on both, with a different size or ETag.
  • failed counts object versions that replication could not copy; pending counts those still waiting. The last failure gives the object and the time.
  • On a versioned target, a plain delete leaves a delete marker, and a resync does not bring the object back. Remove the marker on the target first (mc rm --version-id). The report shows objects that are still missing after the resync.
  • Spot checks on large buckets sample evenly spaced keys. Use sample_size 0 for a full comparison.

How to extend

  • Another MinIO deployment or S3 endpoint: change the secret values; the mc task runner talks to any S3 API.
  • More buckets: add them to buckets, or read the list from a file with a Commands task.
  • Alerting: replace SlackIncomingWebhook with io.kestra.plugin.notifications.* for PagerDuty or email.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.