> ## Content Index
> Fetch the complete content index at: https://madewithlove.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Thanks for all the buckets
- URL: https://madewithlove.com/blog/thanks-for-all-the-buckets/
- Published: 2026-09-25T08:05:07.000Z
- Updated: 2026-09-25T08:05:07.000Z
- Description: MinIO deleted its community edition and pulled its Docker images, breaking every local and CI pipeline that used it to fake S3. Here is the four-step migration to RustFS on a Laravel stack, and why development dependencies deserve the same scrutiny as production ones.
- Author: Novruz Bayramberdiyev
- Tags: Infrastructure, Engineering, Laravel, Tooling, Open source, Testing

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](https://madewithlove.com/blog/mocking-external-systems-in-e2e-tests/) 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](https://madewithlove.com/blog/why-founders-need-to-care-more-about-software-licensing-and-sboms/) that you must request from them.

There are many community forks of MinIO (e.g. [pgsty/silo](https://github.com/pgsty/silo)) and other open-source alternatives, but let's cover the one: [RustFS](https://github.com/rustfs/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](https://laravel.com/framework/docs/filesystem#amazon-s3-compatible-filesystems) mentions RustFS as an example. Laravel Sail now ships a [rustfs stub](https://github.com/laravel/sail/blob/1.x/stubs/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](https://github.com/laravel/sail/pull/899) 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](https://docs.rustfs.com/en/installation) and [migration guide](https://rustfs.com/blog/binary-replacement-a-simple-way-to-migrate-from-minio-to-rustfs/) 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.

```yaml
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:

```makefile
# 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.

```sh
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](https://madewithlove.com/blog/infrastructure-is-a-business-decision/) 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.