Skip to content

RustFS Setup ​

RustFS is an open-source, S3-compatible distributed object store written in Rust and licensed under Apache-2.0. It reached 1.0 in September 2026 and targets the same self-hosted and on-premise uses as MinIO, with erasure coding, lifecycle policies, IAM and server-side encryption.

Use provider: rustfs to select path-style addressing and the us-east-1 default region.

yaml
- name: Cache dependencies using RustFS
  uses: xSAVIKx/cloud-cache-action@v1
  with:
    bucket: ci-cache
    endpoint: https://rustfs.internal:9000
    provider: rustfs
    access-key: ${{ secrets.RUSTFS_ACCESS_KEY }}
    secret-key: ${{ secrets.RUSTFS_SECRET_KEY }}
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-
    path: ~/.npm

Feature support ​

Every feature of this action was verified against RustFS 1.0.0, and the whole integration suite passes against it in CI next to Garage, SeaweedFS and MinIO. Nothing is disabled for this provider:

FeatureRustFS 1.0.0
Multipart upload, with upload-concurrency and upload-chunk-sizeWorks
Ranged GET (parallel downloads)Works, answers 206 Partial Content
User metadata (x-amz-meta-*), so the sha256 integrity check runsStored and returned
Object tags (tags input)Stored, and readable with GetObjectTagging
Conditional create (If-None-Match), for Safe Concurrent SavesEnforced, rejects a second write with 412
CopyObject with REPLACE, for metadata on streaming: true savesWorks
ListObjectsV2 and DeleteObjects, used by the prune sub-actionWork

Port 9000 and provider detection ​

RustFS serves the S3 API on port 9000 by default, the same port MinIO uses, so the endpoint alone cannot tell the two apart. An endpoint ending in :9000 with no provider input is reported as minio in the log. Both presets resolve to the same settings (path-style addressing, us-east-1), so a cache works either way. Set provider: rustfs when you want the log and the cache-storage-provider output to name RustFS.


Credentials ​

The server's initial credentials come from RUSTFS_ACCESS_KEY and RUSTFS_SECRET_KEY, which default to rustfsadmin / rustfsadmin.

WARNING

Do not put the server's root credentials in a workflow. Create a dedicated user restricted to the cache bucket in the RustFS console or through its IAM API, and store that user's keys as GitHub secrets.

The Security guide shows a policy that limits the user to the bucket and the repository prefix.

Secret NameDescription
RUSTFS_ACCESS_KEYAccess key of the CI user
RUSTFS_SECRET_KEYSecret key of the CI user

Example: ephemeral RustFS inside a CI job ​

RustFS starts fast enough to run as a throwaway container in the same job as the build, which gives an isolated cache with no external service:

yaml
name: Build with an ephemeral RustFS cache

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Start RustFS
        run: |
          docker run -d --name ci-rustfs -p 9000:9000 \
            -e RUSTFS_ACCESS_KEY=cloudcacheci \
            -e RUSTFS_SECRET_KEY=cloudcachecisecret \
            --tmpfs /data:uid=10001,gid=10001 \
            --tmpfs /logs:uid=10001,gid=10001 \
            rustfs/rustfs:1.0.0
          # The S3 endpoint answers 403 to an anonymous request once it is ready.
          for _ in $(seq 1 60); do
            code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:9000/ || true)
            if [ "$code" = "403" ]; then echo "RustFS ready."; break; fi
            sleep 1
          done

      - name: Cache dependencies
        uses: xSAVIKx/cloud-cache-action@v1
        with:
          bucket: ci-cache
          endpoint: http://127.0.0.1:9000
          provider: rustfs
          access-key: cloudcacheci
          secret-key: cloudcachecisecret
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          path: ~/.npm

The action creates nothing but objects: create the bucket first, either in the console or with any S3 client, because the action never creates a bucket.

NOTE

The container runs as uid/gid 10001. Give it writable /data and /logs, either as tmpfs mounts as above, as Docker named volumes, or as host directories you chown -R 10001:10001 first. A bind mount the user cannot write makes the server exit at startup with Local disk initialization failed.


Running RustFS locally for integration testing ​

The repository's docker-compose.test.yml includes a RustFS service, published on port 9010 so it can run next to MinIO:

bash
docker compose -f docker-compose.test.yml up -d rustfs
TEST_S3_ENDPOINT=http://127.0.0.1:9010 TEST_S3_PROVIDER=rustfs REQUIRE_S3=1 npm run test:integration
docker-compose.test.yml (Click to view Compose definition)
yaml
name: cloud-cache-test

# Local S3 servers for the integration suite. Credentials match tests/support/s3Server.ts:
#   seaweedfs, minio, rustfs: cloudcacheci / cloudcachecisecret
#   garage: run `bash tests/ci/start-garage.sh` after `up`, then use the key in that script.
services:
  garage:
    image: dxflrs/garage:v2.4.1
    container_name: cloud-cache-garage
    ports:
      - '3900:3900' # S3 API
      - '3903:3903' # Admin API
    volumes:
      - ./tests/fixtures/garage.toml:/etc/garage.toml:ro
    environment:
      - RUST_LOG=info
    restart: unless-stopped

  seaweedfs:
    image: chrislusf/seaweedfs:4.46
    container_name: cloud-cache-seaweedfs
    ports:
      - '8333:8333' # S3 API
    volumes:
      - ./tests/fixtures/seaweedfs-s3.json:/etc/seaweedfs/s3.json:ro
    command: 'server -dir=/data -s3 -s3.port=8333 -s3.config=/etc/seaweedfs/s3.json -volume.max=0 -master.volumeSizeLimitMB=64'
    restart: unless-stopped

  minio:
    # Upstream MinIO no longer publishes community images; CleanStart rebuilds it from source.
    image: cleanstart/minio:0.20260330.001845@sha256:d5dc9098ea6239fc7215165c1496fa026abbb9560bdd7a67c4938e9226394839
    container_name: cloud-cache-minio
    ports:
      - '9000:9000'
    environment:
      - MINIO_ROOT_USER=cloudcacheci
      - MINIO_ROOT_PASSWORD=cloudcachecisecret
    tmpfs:
      - /data:uid=65532,gid=65532
    command: 'server /data'
    restart: unless-stopped

  rustfs:
    # RustFS serves S3 on 9000, the same port MinIO uses, so it is published on 9010 here to let
    # both run at once. The container runs as uid/gid 10001 and needs /data and /logs writable.
    image: rustfs/rustfs:1.0.0
    container_name: cloud-cache-rustfs
    ports:
      - '9010:9000' # S3 API
    environment:
      - RUSTFS_ACCESS_KEY=cloudcacheci
      - RUSTFS_SECRET_KEY=cloudcachecisecret
    tmpfs:
      - /data:uid=10001,gid=10001
      - /logs:uid=10001,gid=10001
    restart: unless-stopped

Live CI ​

The repository runs its integration suite and a full save-and-restore round trip against RustFS on every pull request, in the S3 compatibility (rustfs) job of .github/workflows/test.yml.

Notes ​

  • Checksums: for this provider, the action sends request checksums only when S3 requires them, because many S3-compatible services reject the CRC checksums recent AWS SDKs send by default. Nothing to configure.
  • Expiry: the action never deletes caches. Use a RustFS lifecycle policy, or the prune sub-action, so old caches do not accumulate.