MinIO is a high-performance, self-hosted object storage server compatible with the S3 API — letting you run your own object storage on a VPS instead of paying for third-party storage, while keeping compatibility with S3-compatible tools and libraries.
Why Self-Host Object Storage
- Full control over data location and retention
- Potentially lower cost at scale compared to hosted providers
- S3-compatible API means most existing tools/SDKs work without modification
Prerequisites
- A VPS with adequate storage for your needs
- Docker Engine installed
Step 1 — Run MinIO
docker run -d \
--name minio \
--restart unless-stopped \
-p 9000:9000 \
-p 9001:9001 \
-e MINIO_ROOT_USER=admin \
-e MINIO_ROOT_PASSWORD=CHANGE_ME_STRONG_PASSWORD \
-v minio_data:/data \
minio/minio server /data --console-address ":9001"
Port 9000 is the S3 API endpoint, port 9001 is the web console.
Step 2 — Allow the Required Ports
sudo ufw allow from YOUR_TRUSTED_IP to any port 9001
sudo ufw allow 9000/tcp
Restrict the console (9001) to trusted IPs; the API port (9000) needs broader access if your applications connect from elsewhere.
Step 3 — Access the Web Console
http://YOUR_SERVER_IP:9001
Log in with the root credentials set above.
Step 4 — Create a Bucket
In the console: Buckets → Create Bucket, name it (e.g. myapp-uploads).
Step 5 — Create an Access Key for Applications
Under Access Keys → Create access key — use this for application connections rather than the root credentials.
Step 6 — Add HTTPS
Route both the API and console through Nginx Proxy Manager (see How to Install Nginx Proxy Manager with Docker) with valid SSL — critical since credentials and data transfer over this connection.
Step 7 — Test with the AWS CLI
aws configure --profile minio
aws s3 ls --profile minio --endpoint-url https://storage.yourdomain.com
Uploading a Test File
aws s3 cp test.txt s3://myapp-uploads/ --profile minio --endpoint-url https://storage.yourdomain.com
Setting Bucket Policies
Configure public/private access per bucket through the console under bucket settings — keep buckets private by default, only making specific buckets/paths public if genuinely needed.
Setting Up Lifecycle Rules
In the console, configure automatic expiration/deletion rules per bucket — useful for temporary uploads or enforcing retention policies without manual cleanup scripts.
Backing Up MinIO Data
docker run --rm -v minio_minio_data:/data -v $(pwd):/backup alpine tar czf /backup/minio-backup.tar.gz /data
Alternatively, use MinIO's own replication features to sync to a second MinIO instance for redundancy.
Common Errors
"Access Denied" — verify you're using an access key with appropriate bucket permissions, not attempting an operation the key isn't authorized for.
Slow uploads/downloads — check your VPS's disk I/O performance and network bandwidth; object storage performance is directly tied to underlying disk speed.
Best Practices
- Never use root credentials for application connections — create scoped access keys
- Always run behind HTTPS
- Keep buckets private by default, exposing only what's genuinely needed publicly
Continue Reading
- How to Use Object Storage for Application File Uploads
- How to Back Up to Object Storage (S3-Compatible)
- How to Install Nginx Proxy Manager with Docker
Browse more articles in Object Storage, Messaging & APIs.
