Skip to content

Migrate from MinIO to Silo

What changes, what stays, and how to switch a container deployment. Package installations are covered in Native Package Migration.

Migrating from MinIO to Silo normally reuses existing object data and volumes without an object-by-object export and import. Ordinary S3 applications usually need no code changes; administrators still need to check deployment, authorization and state compatibility. Select the applicable O01–O08 conditions in the three-level overview before following this guide. RPM/DEB installs are covered in Native Package Migration; check the component matrix for version-specific requirements.

What changes

In order of importance:

  1. Container image: minio/minio, quay.io/minio/minio, and pgsty/minio are all replaced by docker.io/pgsty/silo.
  2. Package, systemd service, and server executable: miniosilo.
  3. Upstream services: the in-place updater and MinIO-operated callhome/SUBNET are disabled; upgrades go through packages, images, or your orchestrator.
  4. Default OS service account: silo — fresh installations only; migrations keep running as the existing data owner.
  5. Default local configuration directory: ~/.minio~/.silo (fresh processes only; see server compatibility for the certificate fallback order — an existing ~/.minio/certs keeps being honored).
  6. Branding: banners, Console appearance, log wording, and product links say Silo.

What stays

  • Object layouts, erasure formats and the .minio.sys directory remain, allowing data disks from compatible baselines to be reused. This does not guarantee arbitrary downgrades; see rollback scope.
  • Existing buckets, object versions, users, access keys, policies, lifecycle and encryption configuration remain usable. Authorization decisions, replication state and new metadata have exceptions in O02 and O07; the target release notes list the specifics.
  • Common S3 APIs, SigV4, SDK, mc/mcli and presigned URL integration carry over. Validation, conditional requests and error behavior have changes in O04.
  • Endpoint hostname, API port 9000, Console port, volume mounts.
  • MINIO_* environment variables and existing server options.
  • /minio/* routes, x-minio-* headers, minio_* metrics.
  • Policy-namespace identifiers: arn:minio:* ARNs, minio:s3 and other service namespaces in IAM policies, notifications and audit events keep their spelling. Do not rename these strings with the product; still check authorization semantics under O02.

These conclusions apply to the comparison baseline and compatible erasure deployments. For older MinIO versions or historical filesystem/gateway deployments, establish the migration path for that version and deployment mode, then validate it in staging.

Docker migration

Whichever image you run today, replace it with:

docker.io/pgsty/silo:<RELEASE-tag>

Tags: immutable RELEASE.YYYY-MM-DDTHH-MM-SSZ (pin these), rolling latest, and the -distroless variants below. The old pgsty/minio repository stays published, frozen at its final tag.

The Compose example below retains the original ports, data volume and MINIO_* configuration. Before switching, check custom entrypoints, executable paths, runtime users, directory permissions and probes; changing the image name alone does not establish a successful migration.

services:
  minio:                              # service name may stay "minio"
    image: docker.io/pgsty/silo:<RELEASE-tag>
    command: server /data --console-address ":9001"
    environment:                      # MINIO_* unchanged
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    ports: ["9000:9000", "9001:9001"]
    volumes:
      - minio-data:/data              # same volume, same data
volumes:
  minio-data:
docker compose pull minio && docker compose up -d minio

The entrypoint translates the legacy first argument, so an inherited command: minio server /data keeps working. A hard-coded entrypoint: /usr/bin/minio must change to /usr/bin/silo. Existing mc ready local healthchecks keep working; the native replacement is test: ["CMD", "silo", "healthcheck", "ready"] (reference). Do not run docker compose down -v-v deletes the data volume.

Distroless variant

pgsty/silo:<RELEASE-tag>-distroless ships the silo binary only: no shell, no mc, no curl. It has a built-in HEALTHCHECK (the native probe) and works under any --user:

docker run -d --name silo \
  -p 9000:9000 -p 9001:9001 \
  -e MINIO_ROOT_USER=admin \
  -e MINIO_ROOT_PASSWORD=change-me-long-password \
  -v silo-data:/data \
  docker.io/pgsty/silo:<RELEASE-tag>-distroless \
  server /data --console-address ":9001"

The same deployment as a Compose file:

services:
  silo:
    image: docker.io/pgsty/silo:<RELEASE-tag>-distroless
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: admin
      MINIO_ROOT_PASSWORD: change-me-long-password
    ports: ["9000:9000", "9001:9001"]
    volumes:
      - silo-data:/data
volumes:
  silo-data:

depends_on: condition: service_healthy works against it with no healthcheck: block. The volume format is the same as the classic image and MinIO — the variants are interchangeable over the same data. TLS certificates mount at /tmp/.silo/certs. If command-line flags move the listen address, point the built-in probe with MINIO_HEALTHCHECK_URL. There is no shell inside; debug with docker debug / kubectl debug.

Kubernetes

Kubelet probes are httpGet requests in the pod spec; Docker HEALTHCHECK is ignored, so both image variants are probed identically and existing probe configs keep working. For Helm releases, keep the release identity with nameOverride/fullnameOverride and compare helm template output before applying (details).

Rollback

Establish that state can be recovered before switching back. Compatible object layouts do not guarantee that old software understands new bucket configuration, IAM revisions or deletion history, or that permissions and replication state remain equivalent after rollback.

Before upgrading, retain the original image digest, deployment configuration and a matching recovery point, and test the exact version combination in isolation. For the new IAM revocation mechanism, follow IAM upgrade and recovery to preserve complete IAM storage and key material; ordinary administration exports omit deletion history. Also check bucket-configuration deletion state and password permissions.

Use the planned image or binary rollback only when the relevant upgrade instructions permit it and recovery validation passes. Versions with durable IAM revocation do not support rolling downgrade. Do not let old and new nodes access the same data simultaneously. Restoring an older snapshot also requires handling data and authorization changes made after that snapshot.

Upgrading from RELEASE.2026-08-06

The release after RELEASE.2026-08-06T00-00-00Z tightens several behaviors that 20260806 accepted. Check these before upgrading:

  1. Explicit version deletes need s3:DeleteObjectVersion. DeleteObject and DeleteObjects entries that carry a versionId are authorized as s3:DeleteObjectVersion, as on AWS. Grant it to principals that delete specific versions, and add Deny s3:DeleteObjectVersion next to any Deny s3:DeleteObject that is meant to block permanent deletes.
  2. Enable and disable are separate admin actions. admin:EnableUser / admin:DisableUser and the group equivalents are checked against the requested status; a policy that grants only one of them loses the other operation.
  3. New and updated policies reject bare ARN prefixes such as arn:aws:s3:::, and statements that combine Resource with NotResource. Stored policies keep loading; automation that re-applies such policies fails.
  4. Legacy database notification targets need a connection string. An enabled pre-KV PostgreSQL or MySQL target without connection_string / dsn_string stops startup with a credential-free error; 20260806 silently dropped every notification target in that situation.
  5. Checksum requests are validated. Unknown x-amz-checksum-* algorithms, CRC64NVME combined with COMPOSITE, and checksum-type assertions that contradict the upload are rejected with 400. The default behavior of the AWS SDKs, minio-go, and mcli is unaffected.
  6. Per-bucket CORS is real. A bucket with its own CORS configuration is served by that configuration only; MINIO_API_CORS_ALLOW_ORIGIN applies to buckets without one. In a site-replication group, configure bucket CORS only after every site runs the new release: older peers accept but ignore the configuration and keep reporting a CORS mismatch.
  7. Rollback loses new CORS configuration. 20260806 ignores bucket CORS and drops it when rewriting bucket metadata. Export it before rollback and restore it after upgrading again; check other state separately under rollback scope.

Console regressions in the September 3 release

RELEASE.2026-09-03T13-18-01Z stopped recognizing forwarded client addresses from unconfigured local proxies and discarded the four CONSOLE_WS_MAX_* connection settings. This can change IP Allow/Deny decisions and prevent operators from raising the eight-connection anonymous per-address limit.

Builds containing the fixes restore loopback TCP-peer trust for embedded Console unless MINIO_API_TRUSTED_PROXIES=none/off, and preserve all four limits from the environment or MINIO_CONFIG_ENV_FILE. Remote proxies still need an explicit IP/CIDR list. Standalone defaults, forwarded-chain trust rules and connection budgets remain unchanged; invalid configuration becomes a startup error. See Console settings for the policy table and configuration constraints. Track availability in #147 and #148; the 0903 image does not include these fixes.

On 0903, explicitly listing the local proxy peer in MINIO_API_TRUSTED_PROXIES restores client attribution, but also switches the S3 listener on port 9000 to listed mode; include its other required proxies too. Custom WebSocket limits require a fixed Server build or standalone Console.

One cluster, one binary

Distributed nodes verify each other’s binary at bootstrap. A node started among peers running a different binary does not fail — it waits indefinitely in activating, logging:

Expected Silo binary checksum: ..., seen: ...
Waiting for at least 1 remote servers with valid configuration to be online

This applies to any pair of different binaries: MinIO next to Silo, and one Silo version next to another. So do not migrate — or later upgrade — a cluster node by node. Switch all nodes in one pass: stop the old binary everywhere, start the new one everywhere (in Compose: change the image for all nodes in one edit, docker compose up -d once). Single-node deployments are unaffected. The same applies to rollback. Rolling restarts of the same binary work normally; gate them with silo healthcheck --maintenance cluster (exit 0 = safe to stop this node).

Verification

Before touching anything, record the artifacts you are leaving behind: the running image digest (or package version and binary checksum), the unit status and enabled state, and the UID/GID that owns the data directory. Rollback is only as precise as that record.

silo healthcheck ready                   # this node serves; exit 0/1
silo healthcheck cluster                 # cluster-wide write quorum
mc admin info <existing-alias>           # all nodes online, new version, old alias

Then download a known object and compare its checksum, exercise one application through its existing SDK, restart the service once, and re-check. One more rollback precondition: do not enable features in the migration window that the old version cannot understand — rolling back means rolling back to what the old binary can parse.