2026-05-07 update — MinIO’s open-source community edition is archived. As of April 25, 2026, the MinIO GitHub repo is read-only and there are no more security patches. The web console was removed back in May 2025. The MinIO half of this comparison is still useful for understanding the space, but don’t deploy fresh MinIO today — use Garage instead. SeaweedFS is still actively maintained and the recommendations here for it stand.
Your App Wants S3 But Your Wallet Wants Out
Here’s the situation: you’re building something — a media server, a document archive, a home automation system, a backup target. The application you’re using expects S3-compatible object storage. Maybe it’s a backup tool like Restic. Maybe it’s Immich for your photo library. Maybe it’s your application code that you wrote to expect boto3 and an S3 endpoint.
You could use AWS S3. You’d have the world’s most reliable storage. You’d also pay per GB stored, per GET request, per PUT request, per GB transferred out. At small scale it’s pennies. At home-lab scale with lots of writes and reads it accumulates. And there’s something philosophically satisfying about not routing your personal photos through Amazon’s infrastructure.
Two tools dominate the self-hosted S3-compatible storage space: MinIO and SeaweedFS. Both expose an S3-compatible API. Both are written in Go. Both work with basically anything that speaks S3. They have very different design philosophies, and understanding the differences helps you pick the right one.
What S3 Compatibility Actually Means
The S3 API is a de facto standard for object storage. Applications that support S3 can typically point at any S3-compatible endpoint and work identically. The core operations:
PutObject— upload a fileGetObject— download a fileDeleteObject— delete a fileListBuckets/ListObjects— browse buckets and contents- Bucket policies, ACLs, versioning (varies by implementation)
If you’ve ever used aws s3 cp or Python’s boto3, you can use exactly the same commands against MinIO or SeaweedFS — just change the endpoint URL and credentials.
MinIO: The Enterprise-Grade Option
MinIO describes itself as “High Performance Object Storage.” It’s fully S3 API compatible (including advanced features like versioning, object locking, lifecycle policies), ships with a polished web console, has excellent documentation, and comes in both an open-source AGPLv3 version and a commercial version.
For home labs and small production deployments, the open-source version is more than sufficient.
Single-Node Docker Compose Setup
version: '3.8'
services: minio: image: minio/minio:latest container_name: minio command: server /data --console-address ":9001" ports: - "9000:9000" # S3 API - "9001:9001" # Web console environment: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: changethispassword volumes: - minio-data:/data restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3
volumes: minio-data:docker compose up -d
# Access web console at http://your-server:9001# S3 API endpoint: http://your-server:9000Configuring MinIO via mc (MinIO Client)
# Install mccurl https://dl.min.io/client/mc/release/linux-amd64/mc \ --create-dirs -o /usr/local/bin/mcchmod +x /usr/local/bin/mc
# Add your MinIO instance as an aliasmc alias set myminio http://localhost:9000 minioadmin changethispassword
# Create a bucketmc mb myminio/mybucket
# Upload a filemc cp /path/to/file myminio/mybucket/
# List contentsmc ls myminio/mybucket
# Set a bucket to be publicly readablemc anonymous set public myminio/mybucket
# Enable versioningmc version enable myminio/mybucketBucket Policies
MinIO supports S3-style bucket policies in JSON:
cat > policy.json << 'EOF'{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": {"AWS": ["arn:aws:iam::appuser:root"]}, "Action": ["s3:GetObject", "s3:PutObject"], "Resource": ["arn:aws:s3:::mybucket/*"] } ]}EOF
mc admin policy create myminio app-policy policy.jsonmc admin user add myminio appuser secretpasswordmc admin policy attach myminio app-policy --user appuserUsing MinIO with Python boto3
import boto3
s3 = boto3.client( 's3', endpoint_url='http://your-server:9000', aws_access_key_id='minioadmin', aws_secret_access_key='changethispassword', region_name='us-east-1' # Required but any value works)
# Uploads3.upload_file('/path/to/file.txt', 'mybucket', 'file.txt')
# Downloads3.download_file('mybucket', 'file.txt', '/tmp/file.txt')
# Listresponse = s3.list_objects_v2(Bucket='mybucket')for obj in response.get('Contents', []): print(obj['Key'])Distributed Mode (Multi-Node)
MinIO supports distributed deployments for erasure coding and high availability:
# Start a 4-node distributed MinIO (run on each node)minio server http://minio{1...4}/data{1...4}
# Or with Docker Swarm (multi-node compose)# Check MinIO's documentation for the current distributed setup syntax# as it's evolved across versionsSeaweedFS: Built for Scale and Small Files
SeaweedFS was designed from the ground up for a specific problem: storing vast numbers of small files efficiently. Where traditional filesystems and even most object storage systems struggle with millions of tiny files, SeaweedFS handles them gracefully through a clever architecture separating metadata from actual data storage.
It exposes an S3-compatible API, FUSE mount, POSIX interface, and its own native API. It’s lighter-weight than MinIO, uses less RAM in idle state, and scales horizontally without MinIO’s licensing considerations.
Architecture Overview
SeaweedFS has three components:
- Master server: Handles metadata and volume management
- Volume server: Stores actual data in volumes (large binary files containing many small files)
- Filer: Optional layer providing directory/file semantics and S3 API
Clients → Filer (S3 API / FUSE) → Master (metadata) → Volume servers (data)Docker Compose Setup
version: '3.8'
services: seaweedfs-master: image: chrislusf/seaweedfs:latest container_name: seaweedfs-master command: master -ip=seaweedfs-master -port=9333 ports: - "9333:9333" volumes: - seaweedfs-master:/data restart: unless-stopped
seaweedfs-volume: image: chrislusf/seaweedfs:latest container_name: seaweedfs-volume command: volume -mserver=seaweedfs-master:9333 -port=8080 -dir=/data ports: - "8080:8080" volumes: - seaweedfs-volume:/data depends_on: - seaweedfs-master restart: unless-stopped
seaweedfs-filer: image: chrislusf/seaweedfs:latest container_name: seaweedfs-filer command: filer -master=seaweedfs-master:9333 -port=8888 -s3 -s3.port=8333 ports: - "8888:8888" # Filer API - "8333:8333" # S3 API depends_on: - seaweedfs-master - seaweedfs-volume restart: unless-stopped
volumes: seaweedfs-master: seaweedfs-volume:Using the S3 API
SeaweedFS’s S3 API works with standard S3 tools:
# With mcmc alias set weed http://localhost:8333 "" ""# SeaweedFS S3 doesn't require auth by default (configure it for production)
mc mb weed/mybucketmc cp /path/to/file.jpg weed/mybucket/
# With AWS CLIaws configure --profile weed# Access Key: any (or empty)# Secret Key: any (or empty)# Region: us-east-1
aws --profile weed --endpoint-url http://localhost:8333 \ s3 cp /path/to/file.jpg s3://mybucket/FUSE Mount
# Install weed CLI# Mount a bucket as a local directoryweed mount -filer=localhost:8888 -dir=/mnt/seaweed -collection=mybucketThis mounts your SeaweedFS storage as a regular directory. Applications can read/write files without knowing anything about S3.
MinIO vs SeaweedFS: The Comparison
| Feature | MinIO | SeaweedFS |
|---|---|---|
| Language | Go | Go |
| S3 Compatibility | Full | Good (common operations) |
| Bucket policies | Full S3 IAM | Basic |
| Versioning | Yes | Yes (filer) |
| Object locking (WORM) | Yes | Partial |
| Web console | Excellent | Basic |
| Small file handling | Good | Excellent |
| FUSE mount | Via mc | Native |
| Memory footprint (idle) | ~100-200MB | ~50-100MB |
| Horizontal scale | Distributed mode | Built-in multi-volume |
| Enterprise features | Yes (tiering, encryption) | Moderate |
| Documentation | Excellent | Good |
| License | AGPL v3 | Apache 2.0 |
| Best for | Single/multi-node S3 storage | Millions of small files |
Which One for Your Use Case?
MinIO if:
- You need full S3 API compatibility (bucket policies, versioning, lifecycle rules)
- You’re integrating with enterprise tools that expect S3
- You want a polished web UI for bucket management
- You’re storing large files (videos, databases, backups)
- Your team is familiar with S3
SeaweedFS if:
- You need to store millions of small files (images, thumbnails, documents)
- You want a FUSE mount for simple filesystem access
- Lower memory footprint matters
- You want Apache 2.0 licensing
- You’re building a distributed system that needs to scale across many nodes
For most home labs: MinIO. It’s easier to set up, better documented, has a nicer UI, and works with everything out of the box. The web console alone makes it worth it when you want to quickly browse what’s in your buckets.
For large-scale photo archives or document storage where you’ve got hundreds of thousands of small files: look seriously at SeaweedFS.
Either way, self-hosted S3 means your data stays on your hardware, your costs are predictable, and you stop getting itemized AWS bills that list exactly how many times you accessed your own files. That’s worth an afternoon of setup.