3.6 KiB
Object storage migration
RustFS is the normal S3-compatible store. MinIO and storage_migrate are gated
behind the storage-migration Compose profile and are not started by a normal
docker compose up -d.
On the first production release with a legacy MinIO container, the deployment
workflow pauses app/scheduler writers, starts both stores, copies every bucket
and its current objects (including mineseeker media and LiipImagine cache
thumbnails), verifies each source/target key-and-size inventory, then starts
the app on RustFS. It also carries over bucket policies, while enforcing the
existing public-read policy for mineseeker/media/* and mineseeker/cache/*.
Versioned or suspended-versioning buckets fail safely instead of silently
losing historical versions/delete markers. Only after successful migration
and app startup does it remove the
MinIO and migration containers. It never removes the named minio_data volume;
keep that volume as the rollback/backup until RustFS has been verified and a
separate retirement decision is made. A missing source bucket or inventory
mismatch fails the deployment rather than recording an empty/partial migration.
RustFS retains the public host ports 9010 (S3 API) and 9011 (console); the
legacy MinIO temporarily uses 9012 and 9013. Confirm the public storage
hostname configured by MINIO_PUBLIC_URL (for example, d.aws...) proxies to
the RustFS API port before serving the new app. The legacy MinIO uses the local
image named by MINIO_IMAGE (defaulting to the previously configured release
tag) and does not try to pull it. If production has another local image tag,
set MINIO_IMAGE to that exact reference before deployment.
For future releases, the workflow sees no MinIO container and deploys only the
RustFS stack. Review the release logs and docker compose logs rustfs after
rollout; never use docker compose down -v as part of migration/retirement.
Testing a backup in isolation
bin/restore-minio.sh erases the MinIO volume belonging to the selected Compose
project before extracting the archive. Test with a separate project name and
APP_NAME; Compose then uses separate containers, network, and named volumes.
Use alternate published ports so the test services can run beside the regular
development stack:
export COMPOSE_PROJECT_NAME=mine-minio-test
export APP_NAME=mine-minio-test
export MINIO_PUBLISHED_API_PORT=19102
export MINIO_PUBLISHED_CONSOLE_PORT=19103
export RUSTFS_PUBLISHED_API_PORT=19100
export RUSTFS_PUBLISHED_CONSOLE_PORT=19101
export APP_PUBLISHED_PORT=19180
export POSTGRES_PUBLISHED_PORT=15452
export MAILHOG_SMTP_PUBLISHED_PORT=19125
export MAILHOG_UI_PUBLISHED_PORT=19126
docker compose --profile storage-migration up -d minio rustfs
bin/restore-minio.sh bazmeg/mineseeker-minio.2026-10-24.162934.tar.gz
docker compose --profile storage-migration up --build --abort-on-container-exit --exit-code-from storage_migrate storage_migrate
docker compose logs storage_migrate
The restore confirmation names the test MinIO volume; verify it says
mine-minio-test_minio_data before typing yes. The migration's state volume
is also isolated. This starts only MinIO and RustFS before the explicit
migration—not the app, scheduler, or database. To start the test app only after
the migration succeeds, restore its PostgreSQL backup, then run
MINIO_PUBLIC_URL=http://localhost:19100 docker compose up -d app scheduler
so browser-generated cache URLs reach the isolated RustFS. docker compose down
does not delete the test volumes; avoid down -v until you intentionally want
to discard the test copy.