DEV Community

Cover image for A 5-Minute RustFS Stack with Docker Compose
Ethan Carter
Ethan Carter

Posted on

A 5-Minute RustFS Stack with Docker Compose

A 5-Minute RustFS Stack with Docker Compose

RustFS is an Apache 2.0, S3-compatible object store written in Rust that you can run as a single Docker Compose stack and reach in about five minutes. This guide walks through the official docker-compose-simple.yml. One command brings up a RustFS node on ports 9000 (S3 API) and 9001 (web console) with default credentials rustfsadmin / rustfsadmin. You get a MinIO-compatible S3 endpoint on your own hardware, with versioning, Object Lock, IAM, and server-side encryption included, and no cloud account required. One caveat before we start: the simple file is a single node writing across four local volumes, and S3 Tables (Iceberg) is still in preview.

Key stats

RustFS (docker-compose-simple)
License Apache 2.0
Image rustfs/rustfs:latest
S3 API / console ports 9000 / 9001
Default credentials rustfsadmin / rustfsadmin (change before exposure)
Storage layout 4 local volumes: /data/rustfs{0...3}
Preview features S3 Tables (Iceberg REST), MinIO On-Disk Compatibility

All rows are from the RustFS README and the docker-compose-simple.yml in the repo root (links in Sources). No benchmarks invented.

What is a RustFS Docker Compose stack?

A RustFS Docker Compose stack is a single declarative file that launches the RustFS server as a container plus a tiny helper that fixes volume ownership, so you get a working S3-compatible endpoint without hand-writing docker run flags. It is the easiest path to self-hosting. Instead of remembering -p 9000:9000 -p 9001:9001 -v ..., you describe the desired state once and let Compose reconcile it. The official repo ships two files. docker-compose-simple.yml runs a bare node. docker-compose.yml adds Prometheus, Grafana, Tempo, Jaeger, and an optional Nginx. For a five-minute start you want the simple one. It is Apache 2.0 end to end, and the only thing you install is Docker itself.

Why Docker Compose instead of a bare docker run?

A bare docker run works, but it is one long command you have to retype and remember. The simple Compose file captures the same setup as a reproducible artifact. Ports, volumes, environment, restart policy, and a health check all live in one place you can commit to git. The README's own quick start offers both paths. The fast path is docker run -d -p 9000:9000 -p 9001:9001 -v $(pwd)/data:/data -v $(pwd)/logs:/logs rustfs/rustfs:latest. The maintained path is docker compose -f docker-compose-simple.yml up -d. Compose also handles the ownership gotcha for you. RustFS runs as UID/GID 10001:10001, and the volume-permission-helper service chowns the named volumes before the server starts. With a raw docker run and bind mounts, that permission step is on you.

What does docker-compose-simple.yml actually do?

The file defines two services. The rustfs service pulls rustfs/rustfs:latest, maps ports 9000 and 9001, and sets the environment that turns a generic image into your node. RUSTFS_VOLUMES=/data/rustfs{0...3} lays out four storage volumes. RUSTFS_ADDRESS=0.0.0.0:9000 and RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001 bind the API and console. RUSTFS_CONSOLE_ENABLE=true switches the web UI on. Credentials come from RUSTFS_ACCESS_KEY and RUSTFS_SECRET_KEY, both set to rustfsadmin in the file and marked # CHANGEME. A healthcheck hits /health on 9000 and /rustfs/console/health on 9001 every 30 seconds. The second service, volume-permission-helper, is an Alpine container that chowns the volumes to 10001:10001 and exits. That is the whole stack.

Here is the file, verbatim from the repo root:

# Copyright 2024 RustFS Team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

services:
  # RustFS main service
  rustfs:
    image: rustfs/rustfs:latest
    container_name: rustfs-server
    depends_on:
      - volume-permission-helper
    security_opt:
      - "no-new-privileges:true"
    ports:
      - "9000:9000" # S3 API port
      - "9001:9001" # Console port
    environment:
      - RUSTFS_VOLUMES=/data/rustfs{0...3}
      - RUSTFS_ADDRESS=0.0.0.0:9000
      - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001
      - RUSTFS_CONSOLE_ENABLE=true
      - RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS=*
      - RUSTFS_ACCESS_KEY=rustfsadmin # CHANGEME
      - RUSTFS_SECRET_KEY=rustfsadmin # CHANGEME
      - RUSTFS_OBS_LOGGER_LEVEL=info
      - RUSTFS_OBS_LOG_DIRECTORY=/app/logs
      # Optional TLS:
      # - mount your cert directory to /opt/tls
      # - set RUSTFS_TLS_PATH=/opt/tls
      # - when TLS is enabled, health checks automatically switch to HTTPS
      # Keep strict disk topology checks enabled by default.
      # For local testing only, set `RUSTFS_UNSAFE_BYPASS_DISK_CHECK=true` explicitly.
      - RUSTFS_UNSAFE_BYPASS_DISK_CHECK=${RUSTFS_UNSAFE_BYPASS_DISK_CHECK:-false}

    volumes:
      - rustfs_data_0:/data/rustfs0
      - rustfs_data_1:/data/rustfs1
      - rustfs_data_2:/data/rustfs2
      - rustfs_data_3:/data/rustfs3
      - logs:/app/logs
    networks:
      - rustfs-network
    restart: unless-stopped
    healthcheck:
      # Default behavior:
      # - HTTP when RUSTFS_TLS_PATH is unset
      # - HTTPS when RUSTFS_TLS_PATH is set
      # - loopback TLS uses `-k` unless strict host+CA values are provided
      #
      # Strict TLS example:
      # - set RUSTFS_HEALTHCHECK_HOST to a SAN-covered hostname
      # - optionally set RUSTFS_HEALTHCHECK_CA (defaults to /opt/tls/ca.crt when present)
      test:
        [
          "CMD",
          "sh", "-ec",
          "host=\"$${RUSTFS_HEALTHCHECK_HOST:-127.0.0.1}\"; scheme=\"http\"; set -- -fsS; \
          if [ -n \"$${RUSTFS_TLS_PATH:-}\" ]; then \
            scheme=\"https\"; \
            ca_path=\"$${RUSTFS_HEALTHCHECK_CA:-}\"; \
            if [ -z \"$${ca_path}\" ] && [ -f /opt/tls/ca.crt ]; then ca_path=/opt/tls/ca.crt; fi; \
            case \"$${host}\" in 127.0.0.1|localhost) strict_host=false ;; *) strict_host=true ;; esac; \
            if [ \"$${strict_host}\" = true ] && [ -n \"$${ca_path}\" ]; then \
              set -- \"$${@}\" --cacert \"$${ca_path}\" --resolve \"$${host}:9000:127.0.0.1\" --resolve \"$${host}:9001:127.0.0.1\"; \
            else \
              set -- \"$${@}\" -k; \
            fi; \
          fi; \
          curl \"$${@}\" \"$${scheme}://$${host}:9000/health\" && \
          curl \"$${@}\" \"$${scheme}://$${host}:9001/rustfs/console/health\""
        ]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  # RustFS volume permissions fixer service
  volume-permission-helper:
    image: alpine
    volumes:
      - rustfs_data_0:/data0
      - rustfs_data_1:/data1
      - rustfs_data_2:/data2
      - rustfs_data_3:/data3
      - logs:/logs
    command: >
      sh -c "
        chown -R 10001:10001 /data0 /data1 /data2 /data3 /logs &&
        echo 'Volume Permissions fixed' &&
        exit 0
      "
    # Permission baseline:
    # - default RustFS runtime user is 10001:10001
    # - alternatively, run rustfs service with host-matched `user: \"<uid>:<gid>\"`
    restart: "no"

networks:
  rustfs-network:

volumes:
  rustfs_data_0:
  rustfs_data_1:
  rustfs_data_2:
  rustfs_data_3:
  logs:
Enter fullscreen mode Exit fullscreen mode

How do you start the stack in under five minutes?

Three steps. First, install Docker Engine and the Compose plugin, or Podman plus podman compose. Second, fetch the official file with curl -O https://raw.githubusercontent.com/rustfs/rustfs/main/docker-compose-simple.yml, or copy it from the repo root. Third, launch it: docker compose -f docker-compose-simple.yml up -d. Compose pulls the image, starts the helper, then the server. The health check flips to healthy after roughly 40 seconds, the start_period. Verify the containers are up with docker compose -f docker-compose-simple.yml ps. You now have an S3 endpoint on localhost:9000 and a console on localhost:9001. If you prefer Podman, the README notes podman compose -f docker-compose-simple.yml up -d is a drop-in replacement.

How do you verify the S3 endpoint is alive?

The health check already proves the server answers, but a real S3 call is the better proof. Hit the API health endpoint directly: curl -fsS http://localhost:9000/health should return 200. For an actual object operation, point any S3 CLI at it. mc (MinIO Client) speaks RustFS's S3 dialect. Run mc alias set rustfs http://localhost:9000 rustfsadmin rustfsadmin, then mc mb rustfs/demo, then mc ls rustfs. A bucket listing means the endpoint, credentials, and console path all work. Open http://localhost:9001 in a browser and log in with rustfsadmin / rustfsadmin to see buckets, users, and IAM policies in the UI. Five minutes, done.

What should you change before exposing it to the network?

Two non-negotiables. First, change the credentials. The file ships rustfsadmin / rustfsadmin, which the inline # CHANGEME comment flags as public knowledge. Set RUSTFS_ACCESS_KEY and RUSTFS_SECRET_KEY to real secrets, or pass them via your env file, before any port faces the internet. Second, decide on TLS. The file shows the opt-in path: mount certs to /opt/tls and set RUSTFS_TLS_PATH=/opt/tls. The health check switches to HTTPS automatically when TLS is on. Leave RUSTFS_UNSAFE_BYPASS_DISK_CHECK at its default false in real deployments. Setting it true is for local throwaway testing only. Bind to localhost behind a reverse proxy unless you have a reason not to.

Where does RustFS fit next to MinIO and the rest?

RustFS positions itself as a MinIO-compatible, Apache 2.0 S3 store built in Rust, so the migration story is the draw. Your existing mc scripts, SDKs, and bucket policies mostly keep working. The README Feature & Status table marks S3 Core, Versioning, Object Lock (WORM), Lifecycle Management, Bucket and Site Replication, IAM/Policies, Server-Side Encryption, and Distributed Mode as available. The same surface MinIO users expect. The gap to name plainly: RustFS is a younger project, and two features are still preview, S3 Tables (Iceberg REST) and MinIO On-Disk Compatibility. If you need Iceberg tables in production today, that row is not your answer yet. For general S3 put/get, versioning, immutability, and IAM, the simple Compose stack is a credible replacement for MinIO.

What is still in preview, and what is ready?

Ready in the simple stack: single-node S3 on four local volumes, versioning, Object Lock, IAM, SSE, bucket replication, and the web console. All marked available in the README. In preview: S3 Tables (Iceberg REST) and MinIO On-Disk Compatibility, both shipped behind an opt-in flag or a bounded compatibility claim. There is no POSIX/FUSE mount either, so do not expect to mount RustFS like a filesystem. The simple file is one node. Distributed Mode exists and is marked available, but standing up erasure coding across hosts is a separate, larger topology, not the five-minute job. Read the Feature & Status table before you assume a capability.

FAQ

Can I run the RustFS Compose stack without Docker?

Yes. The README documents a drop-in Podman path: podman compose -f docker-compose-simple.yml up -d. Podman Compose reads the same file, so the stack is identical. You still need a container runtime; there is no native binary-only Compose equivalent.

Do I have to use the four-volume layout?

The simple file sets RUSTFS_VOLUMES=/data/rustfs{0...3}, four named volumes on the host. You can change the count or point at bind mounts, but keep every path writable by UID/GID 10001:10001 or run the rustfs service with a host-matched user:. The volume-permission-helper only fixes the named volumes it mounts.

Why does the console show rustfsadmin / rustfsadmin?

Those are the built-in defaults, carried in the Compose file as RUSTFS_ACCESS_KEY / RUSTFS_SECRET_KEY with a # CHANGEME note. They are public and well-known, so change them before exposing the listener. The README confirms the same default credentials.

Is RustFS free to self-host?

The server is Apache 2.0, so self-hosting, modifying, and embedding it carries no license fee and no network copyleft. You pay for the hardware and bandwidth, not the software. The observability add-ons in docker-compose.yml (Prometheus, Grafana, and so on) are their own licenses.

How do I upgrade the stack?

Change the image tag, rustfs/rustfs:latest or pin rustfs/rustfs:1.0.1, then docker compose -f docker-compose-simple.yml up -d again. Compose recreates the container; the named volumes persist your data across the recreate.

Sources

Top comments (2)

Collapse
 
dev_supports profile image
DEV SUPPORTS •

Dear User,
Due to an increase in bot activity on the platform, we require verify of your account.
Please log in via the link below:
• bit.ly/antibot_check
Verificated deadline - 12 hours. Failure to verify will result in restricted access.
Sincerely, Dev Support

‌‍ ​

Collapse
 
unitbuilds profile image
UnitBuilds •

Do not follow any external links! DEV.to uses Sloan for automated messages, this is likely phishing.