#!/usr/bin/env bash set -euo pipefail # Migrate S3 buckets from Hetzner Object Storage to Backblaze B2 via rclone. # # Prerequisites: # 1. Install rclone: https://rclone.org/install/ # 2. Configure two rclone remotes: # rclone config create hetzner s3 \ # provider=Other \ # env_auth=false \ # access_key_id=YOUR_HETZNER_KEY \ # secret_access_key=YOUR_HETZNER_SECRET \ # endpoint=fsn1.your-objectstorage.com # adjust region # # rclone config create backblaze s3 \ # provider=Other \ # env_auth=false \ # access_key_id=YOUR_B2_KEY \ # secret_access_key=YOUR_B2_APP_KEY \ # endpoint=s3.us-west-004.backblazeb2.com # adjust region # # Usage: # ./migrate-s3-hetzner-to-backblaze.sh [src:dst] ... # ./migrate-s3-hetzner-to-backblaze.sh hetzner-bucket:backblaze-bucket HETZNER_REMOTE="${HETZNER_REMOTE:-hetzner}" BACKBLAZE_REMOTE="${BACKBLAZE_REMOTE:-backblaze}" RCLONE_FLAGS="${RCLONE_FLAGS:---transfers=64 --checkers=64 --fast-list}" if [[ $# -eq 0 ]]; then echo "Usage: $0 [src-bucket:dst-bucket...]" echo "" echo " Each argument is a source:destination bucket pair separated by a colon." echo "" echo "Environment variables:" echo " HETZNER_REMOTE rclone remote name for Hetzner (default: hetzner)" echo " BACKBLAZE_REMOTE rclone remote name for Backblaze (default: backblaze)" echo " RCLONE_FLAGS extra rclone flags (default: --transfers=16 --checkers=16 --fast-list)" echo " DRY_RUN=1 show what would be copied without copying" exit 1 fi for cmd in rclone; do if ! command -v "$cmd" &>/dev/null; then echo "Error: $cmd is not installed." >&2 exit 1 fi done # Verify remotes exist for remote in "$HETZNER_REMOTE" "$BACKBLAZE_REMOTE"; do if ! rclone listremotes | grep -q "^${remote}:$"; then echo "Error: rclone remote '${remote}' not found. Run 'rclone config' to set it up." >&2 exit 1 fi done DRY_RUN_FLAG="" if [[ "${DRY_RUN:-0}" == "1" ]]; then DRY_RUN_FLAG="--dry-run" echo "=== DRY RUN MODE ===" fi for pair in "$@"; do if [[ "$pair" != *:* ]]; then echo "Error: '$pair' is not a valid src:dst pair. Use format 'hetzner-bucket:backblaze-bucket'." >&2 exit 1 fi src_bucket="${pair%%:*}" dst_bucket="${pair#*:}" src="${HETZNER_REMOTE}:${src_bucket}" dst="${BACKBLAZE_REMOTE}:${dst_bucket}" echo "" echo "--- Migrating: ${src} -> ${dst} ---" # shellcheck disable=SC2086 rclone sync \ ${RCLONE_FLAGS} \ ${DRY_RUN_FLAG} \ --progress \ "$src" "$dst" echo "--- Done: ${src_bucket} -> ${dst_bucket} ---" done echo "" echo "Migration complete."