MinIO: self-hosted S3-compatible storage for developers and businesses
MinIO is an object storage service compatible with Amazon S3 APIs, but runs on your own server. Docker setup in 5 minutes, bucket policies, Python/FastAPI integration, backup and AI RAG.
Published: June 3, 2025
If you use AWS S3 for file storage, you can replicate the exact same interface on your own server with MinIO. Boto3, S3 SDKs in any language, AWS CLI clients — they all work without code changes. You only change the endpoint. Cost: zero.
Docker setup in 5 minutes
A minimal docker-compose.yml to get MinIO running with a web console:
services:
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
- MINIO_ROOT_USER=admin
- MINIO_ROOT_PASSWORD=password123
volumes:
- minio_data:/data
ports:
- "9000:9000" # S3 API
- "9001:9001" # Web console
restart: unless-stopped
volumes:
minio_data:
docker compose up -d, then open http://localhost:9001, log in with admin/password123, and create a bucket. The S3 API is available on localhost:9000. In production, change the password and put Caddy in front for HTTPS.
Three real use cases
Automated backups from Python scripts. You have a script that generates reports or CSV exports. Instead of saving to the filesystem, send them to MinIO with boto3:
import boto3
s3 = boto3.client(
's3',
endpoint_url='http://localhost:9000',
aws_access_key_id='admin',
aws_secret_access_key='password123'
)
s3.upload_file('report.pdf', 'backups', 'reports/2025-06-03/report.pdf')
# Download
s3.download_file('backups', 'reports/2025-06-03/report.pdf', 'local_copy.pdf')
The code works identically on real AWS S3 — only endpoint_url changes. Zero lock-in.
File uploads in a FastAPI app. A multipart endpoint that receives a file and saves it to MinIO:
from fastapi import FastAPI, UploadFile
import boto3
app = FastAPI()
s3 = boto3.client('s3', endpoint_url='http://minio:9000',
aws_access_key_id='admin', aws_secret_access_key='password123')
@app.post("/upload")
async def upload(file: UploadFile):
s3.upload_fileobj(file.file, 'uploads', file.filename)
return {"key": file.filename}
Files live on MinIO, the app holds nothing locally, and you can scale to multiple FastAPI instances without state conflicts.
Document source for AI RAG. Open WebUI connects to MinIO via its S3 settings (see the self-hosted AI stack guide). Upload company documents to the bucket, Open WebUI indexes them, and the local model can answer questions about their content. Everything stays on your local network.
Bucket policies: public vs private
For buckets with publicly accessible static assets (images, CSS, fonts), set the policy from the console or via CLI:
mc alias set local http://localhost:9000 admin password123
mc anonymous set public local/static-assets
For buckets with company documents, keep the private policy (default) and generate pre-signed URLs with expiration for temporary sharing:
url = s3.generate_presigned_url(
'get_object',
Params={'Bucket': 'documents', 'Key': 'contract.pdf'},
ExpiresIn=3600 # URL valid for 1 hour
)
The URL works without authentication for 3600 seconds, then expires. Useful for sending links to clients or colleagues without exposing the bucket.
What to do
- Start MinIO with
docker compose up -d, create atestbucket from the console atlocalhost:9001, and do an upload/download with boto3 usingendpoint_url='http://localhost:9000'to verify everything works - Replace filesystem saving in at least one existing Python script with a MinIO upload — you’ll immediately see the advantage when accessing files from multiple machines
- If you use Open WebUI, connect the MinIO bucket as S3 storage in the settings and upload 3–4 company PDFs to test RAG on real documents