MinIO is the S3-compatible object storage you can run locally or on your own servers — same API as AWS S3, but free and open source. Used by companies handling petabytes of data.
Why MinIO?
- S3-compatible — use AWS SDK, works with any S3-compatible tool
- Self-hosted — your data, your servers, no cloud bills
- Fast — 325 GiB/s throughput on standard hardware
- Kubernetes-native — built for cloud-native deployments
- Erasure coding — data protection without RAID
- Versioning, lifecycle, encryption — enterprise features, free
Quick Start (Docker)
docker run -d \
--name minio \
-p 9000:9000 \
-p 9001:9001 \
-v minio-data:/data \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
minio/minio server /data --console-address ":9001"
# Console UI: http://localhost:9001
# API: http://localhost:9000
Node.js Client
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
import {
S3Client,
PutObjectCommand,
GetObjectCommand,
ListObjectsV2Command,
DeleteObjectCommand,
} from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "http://localhost:9000",
region: "us-east-1",
credentials: {
accessKeyId: "minioadmin",
secretAccessKey: "minioadmin",
},
forcePathStyle: true,
});
// Upload a file
await s3.send(new PutObjectCommand({
Bucket: "my-bucket",
Key: "images/photo.jpg",
Body: Buffer.from("file contents"),
ContentType: "image/jpeg",
}));
// Download a file
const { Body } = await s3.send(new GetObjectCommand({
Bucket: "my-bucket",
Key: "images/photo.jpg",
}));
const content = await Body.transformToString();
// List files
const { Contents } = await s3.send(new ListObjectsV2Command({
Bucket: "my-bucket",
Prefix: "images/",
}));
console.log(Contents.map(obj => obj.Key));
// Delete
await s3.send(new DeleteObjectCommand({
Bucket: "my-bucket",
Key: "images/photo.jpg",
}));
Presigned URLs (Direct Upload from Browser)
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
// Generate upload URL (valid for 1 hour)
const uploadUrl = await getSignedUrl(s3, new PutObjectCommand({
Bucket: "uploads",
Key: `avatars/${userId}.jpg`,
ContentType: "image/jpeg",
}), { expiresIn: 3600 });
// Frontend can upload directly to MinIO:
// fetch(uploadUrl, { method: 'PUT', body: file })
// Generate download URL
const downloadUrl = await getSignedUrl(s3, new GetObjectCommand({
Bucket: "uploads",
Key: `avatars/${userId}.jpg`,
}), { expiresIn: 3600 });
Express.js File Upload API
import express from "express";
import multer from "multer";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
const s3 = new S3Client({
endpoint: "http://localhost:9000",
region: "us-east-1",
credentials: { accessKeyId: "minioadmin", secretAccessKey: "minioadmin" },
forcePathStyle: true,
});
app.post("/upload", upload.single("file"), async (req, res) => {
const key = `uploads/${Date.now()}-${req.file.originalname}`;
await s3.send(new PutObjectCommand({
Bucket: "my-app",
Key: key,
Body: req.file.buffer,
ContentType: req.file.mimetype,
}));
res.json({
url: `http://localhost:9000/my-app/${key}`,
key,
size: req.file.size,
});
});
app.listen(3000);
Bucket Policies (Public Read)
# Using MinIO client (mc)
mc alias set local http://localhost:9000 minioadmin minioadmin
# Create bucket
mc mb local/public-assets
# Make bucket publicly readable
mc anonymous set download local/public-assets
# Upload public file
mc cp logo.png local/public-assets/
# Now accessible at: http://localhost:9000/public-assets/logo.png
Docker Compose (Dev Environment)
services:
app:
build: .
environment:
S3_ENDPOINT: http://minio:9000
S3_ACCESS_KEY: minioadmin
S3_SECRET_KEY: minioadmin
S3_BUCKET: my-app
depends_on:
- minio
minio:
image: minio/minio
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
volumes:
- minio-data:/data
# Auto-create bucket on startup
minio-init:
image: minio/mc
depends_on:
- minio
entrypoint: >
/bin/sh -c "
mc alias set local http://minio:9000 minioadmin minioadmin;
mc mb local/my-app --ignore-existing;
mc anonymous set download local/my-app;
"
volumes:
minio-data:
MinIO vs AWS S3 vs Google Cloud Storage
| Feature | MinIO | AWS S3 | GCS |
|---|---|---|---|
| Cost | Free (self-hosted) | Pay per GB/request | Pay per GB/request |
| S3 compatible | Yes (native) | Native | Via interop |
| Self-hosted | Yes | No | No |
| Latency | Local (sub-ms) | ~50-100ms | ~50-100ms |
| Max object | 5TB | 5TB | 5TB |
| Encryption | Yes | Yes | Yes |
| Versioning | Yes | Yes | Yes |
| Replication | Built-in | Cross-region | Cross-region |
Need to scrape data from any website and get it in structured JSON? Check out my web scraping tools on Apify — no coding required, results in minutes.
Have a custom data extraction project? Email me at spinov001@gmail.com — I build tailored scraping solutions for businesses.
Top comments (0)