Skip to content

Upgrading to v1.12.0: MinIO → SeaweedFS

From v1.12.0 the S3 store bundled with Depictio is SeaweedFS (weed mini, Apache-2.0) instead of MinIO, whose community edition no longer receives releases. SeaweedFS cannot read MinIO's files, so the bundled store starts empty on a new volume: dashboards miss their tables and images until you copy the bucket over. MongoDB is untouched and object keys are the same on both sides, so that copy is the whole migration.

Your install What to do
Docker Compose with the bundled store Copy the bucket after the upgrade
Helm chart with the bundled store Export the bucket before helm upgrade
External S3 (DEPICTIO_S3_EXTERNAL_SERVICE=true, docker-compose.no-minio.yaml, Helm s3.enabled: false) Nothing
Fresh install, or the Python package (always used SeaweedFS) Nothing

Renamed settings

Settings are named after S3 rather than MinIO. The old names keep working: an old name is read when its new counterpart is unset, the new name wins when both are set, and the server logs a deprecation warning listing the old names it found.

Before v1.12.0 From v1.12.0
DEPICTIO_MINIO_*: ROOT_USER, ROOT_PASSWORD, BUCKET, PUBLIC_URL, EXTERNAL_SERVICE, VERIFY_TLS, EXTERNAL_HOST / _PORT / _PROTOCOL, SERVICE_NAME / _SERVICE_PORT DEPICTIO_S3_*, same suffixes
Compose service minio s3, with minio as a network alias: http://minio:9000 in CLI configurations still resolves
Compose MINIO_PORT / MINIO_CONSOLE_PORT S3_PORT / S3_CONSOLE_PORT
Helm minio.*, persistence.minio, secrets.minioRootUser / minioRootPassword, global.urlPattern.templates.minio s3.*, persistence.s3, secrets.s3RootUser / s3RootPassword, global.urlPattern.templates.s3
  • Helm still merges the legacy keys and renders the same manifests, with a deprecation warning in the install and upgrade notes. Its ConfigMaps export both prefixes, so a backend image older than v1.12.0 keeps working. The legacy copies will be dropped in a later release.
  • Exception, the development stack: docker-compose.dev.yaml requires DEPICTIO_S3_ROOT_USER and DEPICTIO_S3_ROOT_PASSWORD under their new names in docker-compose/.env.

All storage settings: Environment Reference.

Names that stay minio on purpose

They are part of live state, and renaming them would break an upgrade:

  • Helm: the <release>-minio Deployment and Service and their app: minio selector (a Deployment selector cannot change); the <release>-minio-pvc PVC (a new name would mean an empty volume); the <release>-minio-ingress and -minio-httproute routes and the <release>-minio.<domain> host, which DNS, TLS certificates and presigned URLs point at; the Secret keys MINIO_ROOT_USER / MINIO_ROOT_PASSWORD, which keep the generated password stable across upgrades.
  • Compose: the backup-only minio-backup service and the files docker-compose.backup-minio.yaml, docker-compose.no-minio.yaml and docker-compose.minio-legacy.yaml.

Docker Compose

The old MinIO is started again as a side-car on its untouched minio_data volume, and scripts/migrate_minio_to_seaweedfs.py copies the bucket through the S3 API. Run these steps from a clone of the repository at v1.12.0, which holds both files; uv run provides the script's only dependency, boto3.

  1. Upgrade. --remove-orphans stops the old minio container, which the v1.12.0 file no longer declares; its volume stays.

    docker compose pull
    docker compose up -d --remove-orphans
    
  2. Start the old MinIO on 127.0.0.1:9100:

    docker compose -f docker-compose.yaml \
      -f docker-compose/docker-compose.minio-legacy.yaml up -d minio-legacy
    

    For the development stack, whose data is a bind mount, run MINIO_LEGACY_DATA=./data/minio_data docker compose -f docker-compose.dev.yaml … instead.

  3. Publish the new store on 127.0.0.1:9000. The development stack already does. The root docker-compose.yaml does not: add a temporary override.

    s3-port.yaml
    services:
      s3:
        ports: ["127.0.0.1:9000:9000"]
    
    docker compose -f docker-compose.yaml -f s3-port.yaml up -d s3
    
  4. Copy. Credentials and bucket are read from .env (docker-compose/.env for the development stack). Dry run first, then copy and compare both listings:

    set -a; source .env; set +a
    uv run scripts/migrate_minio_to_seaweedfs.py --target-endpoint http://127.0.0.1:9000 --dry-run
    uv run scripts/migrate_minio_to_seaweedfs.py --target-endpoint http://127.0.0.1:9000 --verify
    

    An interrupted copy resumes with --skip-existing.

  5. Check a dashboard, then clean up: drop the override (docker compose up -d), remove the side-car and, once satisfied, the old volume.

    docker compose -f docker-compose.yaml \
      -f docker-compose/docker-compose.minio-legacy.yaml rm -sf minio-legacy
    docker volume rm "$(docker compose config --format json | jq -r .name)_minio_data"
    

    For the development stack: rm -rf ./data/minio_data.

Rolling back: go back to the previous release's compose files; minio_data is untouched until step 5.

Helm

The chart keeps the <release>-minio names, so the upgrade reuses the same PVC. That PVC is ReadWriteOnce: the old MinIO pod and the new SeaweedFS pod cannot mount it at the same time, so the copy goes through a local directory, and the export must happen before the upgrade.

SECRET=<release>-depictio-secrets
KEY=$(kubectl get secret $SECRET -o jsonpath='{.data.MINIO_ROOT_USER}' | base64 -d)
PASS=$(kubectl get secret $SECRET -o jsonpath='{.data.MINIO_ROOT_PASSWORD}' | base64 -d)

# 1. Before upgrading, while MinIO still runs: export the bucket
kubectl port-forward svc/<release>-minio 9100:9000 &
uv run scripts/migrate_minio_to_seaweedfs.py \
  --source-access-key "$KEY" --source-secret-key "$PASS" --export-dir ./s3-export
kill %1

# 2. Upgrade: SeaweedFS starts on the same PVC and ignores MinIO's files
helm upgrade <release> ./helm-charts/depictio -f values.yaml

# 3. Import into the new store
kubectl port-forward svc/<release>-minio 9000:9000 &
uv run scripts/migrate_minio_to_seaweedfs.py --target-endpoint http://127.0.0.1:9000 \
  --target-access-key "$KEY" --target-secret-key "$PASS" --import-dir ./s3-export --verify
kill %1

# 4. Once checked, reclaim the space of MinIO's old files on the PVC
kubectl exec deploy/<release>-minio -- sh -c 'rm -rf /data/.minio.sys /data/depictio-bucket'

Keep ./s3-export until the new store is checked: it is a full copy taken before the upgrade. With a second bucket on an external S3, you can export there (--target-endpoint https://…) instead of to a local directory.

Copy script options
Option Default
--source-endpoint http://127.0.0.1:9100
--target-endpoint the S3 URL resolved from the Depictio settings, else http://127.0.0.1:9000
--source-access-key, --source-secret-key, --target-access-key, --target-secret-key DEPICTIO_S3_ROOT_USER / DEPICTIO_S3_ROOT_PASSWORD, else the DEPICTIO_MINIO_ROOT_* names
--bucket (--target-bucket for another name on the target) DEPICTIO_S3_BUCKET, else depictio-bucket
--prefix copy only the keys under this prefix
--workers 8 parallel copies
--dry-run / --verify / --skip-existing list only / compare listings after the copy / skip keys already on the target with the same size
--export-dir / --import-dir download the bucket into a directory / upload a directory into it, instead of copying directly

Each endpoint and key can also come from a MIGRATE_SOURCE_* / MIGRATE_TARGET_* variable (MIGRATE_SOURCE_ENDPOINT, …).

What else changes

  • The S3 API stays on port 9000; the health check is GET :9000/healthz instead of /minio/health/live. WebDAV is off.
  • The SeaweedFS admin UI is off in the root compose file and in Helm (s3.adminUI.enabled); the development stack serves it on 127.0.0.1:9001, the former MinIO console port, behind the root keys.
  • Root credentials are applied again from the environment at every start, so changing DEPICTIO_S3_ROOT_* and restarting rotates them, as with MinIO.
  • For very large object counts, pass -volume.index=leveldb through Helm s3.extraArgs to keep the volume index out of memory.