MinIO ran on our laptops and in our CI workflows, pretending to be S3 so our tests could upload files to real buckets. Every pipeline run started it. Every upload test wrote to it. Then one morning in September, the image would not pull.

For context, MinIO is a popular open-source, S3-compatible object storage service. It is was a common choice for local development and CI pipelines that need to test S3 interactions without touching real cloud resources.

MinIO's community edition is gone

In May 2025, they removed the console from the community edition, and the last community release was in September 2025. MinIO archived their GitHub repository in February 2026, but unarchived it shortly after. Finally, they archived it again forever in April 2026. They also removed the Docker Hub images. This means docker pull minio/minio fails with pull access denied. There's still a mirror at quay.io/minio/minio, but its latest tag remains on the September 2025 community release. Hotfix builds still appear on that registry now and then. They are undocumented tags on top of old releases with no support commitment.

MinIO's README now recommends AIStor Free (the free tier of their commercial product). It is proprietary and every deployment needs a licence file that you must request from them.

There are many community forks of MinIO (e.g. pgsty/silo) and other open-source alternatives, but let's cover the one: RustFS.

Laravel adopted RustFS

RustFS is another S3-compatible object storage service that's written in Rust (yes, the name already spoiled the surprise). I think RustFS is the safe choice for Laravel projects because Laravel has already adopted it. The filesystem documentation mentions RustFS as an example. Laravel Sail now ships a rustfs stub (a ready-made service definition). The minio stub was still there when I did the RustFS migration. It offered a service whose image no longer exists. Yours truly sent a PR to remove it. Someone had to.

The main concern is maturity because RustFS is still on release candidates (1.0.0-rc.x at the time of writing). For development and CI, that's an acceptable risk.

The migration

This post only covers migrating Laravel projects. If you're on a different stack, check the official documentation and migration guide for the details.

In a typical Laravel setup, you just need to change four things:

1. docker-compose.yml. Replace the minio service with Sail's rustfs stub. Port 9000 stays the same. Everything is configured with environment variables.

services:
  # ...
  rustfs:
    image: 'rustfs/rustfs:latest'
    ports:
      - '${FORWARD_RUSTFS_PORT:-9000}:9000'
      - '${FORWARD_RUSTFS_CONSOLE_PORT:-9001}:9001'
    environment:
      RUSTFS_VOLUMES: '/data'
      RUSTFS_ADDRESS: '0.0.0.0:9000'
      RUSTFS_CONSOLE_ADDRESS: '0.0.0.0:9001'
      RUSTFS_CONSOLE_ENABLE: 'true'
      RUSTFS_EXTERNAL_ADDRESS: ':9000'
      RUSTFS_CORS_ALLOWED_ORIGINS: '*'
      RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS: '*'
      RUSTFS_ACCESS_KEY: mystaff
      RUSTFS_SECRET_KEY: longersecret
      RUSTFS_LOG_LEVEL: 'info'
    volumes:
      - 'sail-rustfs:/data'
    networks:
      - sail
    healthcheck:
      test: ["CMD", "sh", "-c", "curl -f http://127.0.0.1:9000/health && curl -f http://127.0.0.1:9001/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
  # ...

2. The S3 disk configuration. Point the endpoint to the new service. Keep use_path_style_endpoint enabled, same as MinIO. Local hostnames cannot resolve bucket subdomains, so path-style is required. If your tests override the endpoint in phpunit.xml, update that line too.

3. CI. RustFS runs as a plain health-checked services: entry in GitHub Actions. MinIO couldn't run as a service container because its image requires command arguments, and service containers don't accept them. So our MinIO pipelines had a custom docker run step and a wait-until-healthy loop. If you have similar steps, delete them. Deleting them is the best part of the whole migration.

4. Bucket provisioning. This is the one annoying gap. The MinIO image bundled mc (their CLI tool). Almost every setup, including ours, used mc to create buckets. The RustFS image has no client tools, so you now need the AWS CLI or an SDK. We defined a Makefile target to create buckets that uses a throwaway AWS CLI container:

# The network is named after your compose project: `<folder-name>_sail`
create-buckets:
    @docker run --rm \
        --network cloud_sail \
        -e AWS_ACCESS_KEY_ID=mystaff \
        -e AWS_SECRET_ACCESS_KEY=longersecret \
        -e AWS_DEFAULT_REGION=ams3 \
        -v "$(shell pwd)/.docker/sail/rustfs/create-buckets.sh:/create-buckets.sh" \
        --entrypoint /create-buckets.sh \
        amazon/aws-cli:latest

Temporary URLs

Storage::temporaryUrl() puts the container hostname into the link. Your browser runs on the host machine and cannot resolve that hostname. Add one line to /etc/hosts to fix it.

127.0.0.1 rustfs

MinIO had the same problem, so tradition continues. Some things never change.

Check your projects

We don't audit our development and CI dependencies as much as we do production ones, but they're still dependencies too. They can disappear anytime. Bitnami moved its catalogue behind a Broadcom subscription in late 2025, and now MinIO unpublished its images. Twice in a year is enough to make this look like a pattern rather than bad luck.

For the love of green CI, check your repositories for minio/minio before a fresh docker pull does it for you. If you find it, the migration above takes a couple of hours.