MinIO: Your Own S3 Storage on a VPS
MinIO is a high-performance object storage system fully compatible with the Amazon S3 API. It's ideal for storing media files, backups, static assets, and ML datasets right on your VPS.
Installing via Docker Compose
# /opt/minio/docker-compose.yml
version: '3.8'
services:
minio:
image: minio/minio:latest
container_name: minio
ports:
- "9000:9000" # S3 API
- "9001:9001" # Web Console
volumes:
- /data/minio:/data
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: StrongPassword123!
MINIO_VOLUMES: "/data"
command: server /data --console-address ":9001"
restart: unless-stoppedmkdir -p /data/minio
docker compose up -dNginx Reverse Proxy + SSL
server {
listen 443 ssl;
server_name s3.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/s3.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/s3.yourdomain.com/privkey.pem;
# S3 API
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 0; # no file size limit
proxy_buffering off;
proxy_request_buffering off;
}
}
server {
listen 443 ssl;
server_name minio-console.yourdomain.com;
# ... a similar block for port 9001
location / {
proxy_pass http://127.0.0.1:9001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}Creating Buckets and Users via the CLI
# Install mc (MinIO Client)
curl -O https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc && mv mc /usr/local/bin/
# Connect to the server
mc alias set myminio https://s3.yourdomain.com minioadmin StrongPassword123!
# Create a bucket
mc mb myminio/website-uploads
mc mb myminio/backups
# Make the bucket public (for static assets)
mc policy set public myminio/website-uploads
# Create a user
mc admin user add myminio appuser AppUserPass123
mc admin policy attach myminio readwrite --user appuserIntegration with PHP (AWS SDK)
composer require aws/aws-sdk-php
<?php
use Aws\S3\S3Client;
$s3 = new S3Client([
'version' => 'latest',
'region' => 'us-east-1', // MinIO ignores the region, but the parameter is required
'endpoint' => 'https://s3.yourdomain.com',
'use_path_style_endpoint' => true,
'credentials' => [
'key' => 'appuser',
'secret' => 'AppUserPass123',
],
]);
// Upload file
$result = $s3->putObject([
'Bucket' => 'website-uploads',
'Key' => 'images/photo.jpg',
'SourceFile' => '/tmp/photo.jpg',
'ACL' => 'public-read',
]);
echo $result['ObjectURL'];mc version enable myminio/backups.Real Errors Explained
Two problems show up for almost everyone deploying MinIO on a VPS for the first time.
The Drive Is Not Writable
ERROR Unable to use the drive /data: Drive is not writable, please fix the file system permissionsThe /data/minio directory on the host is owned by root, while the process inside the container runs as an unprivileged user. Check the owner with ls -la /data/minio and fix it with chown -R 1000:1000 /data/minio, or explicitly set user: "1000:1000" in docker-compose.yml.
A Self-Signed Certificate When Connecting with mc
mc: <ERROR> Unable to initialize new alias from the provided credentials. Get "https://s3.yourdomain.com/probe-bucket-sign...": x509: certificate signed by unknown authorityThis usually means nginx in front of MinIO has not yet received a real Let's Encrypt certificate and is serving a temporary self-signed one. Check certbot certificates and the file paths in the nginx config — before the certificate is issued, you can temporarily test mc alias set with the --insecure flag, but never use that in permanent scripts.
MinIO vs. Managed S3 Providers
| Criterion | MinIO on your own VPS | Hetzner Object Storage / R2 |
|---|---|---|
| Control over the data | Full, physically on your own disk | Data lives with the provider |
| Maintenance and updates | Your responsibility | The provider updates and patches it |
| Scaling capacity | Limited by the server's physical disk | Grows automatically without your involvement |
| SOC2/ISO storage certification | Entirely your responsibility | Large providers usually have it |
When MinIO on a VPS Is Not a Good Fit
- You need geo-distributed fault tolerance out of the box — manually setting up MinIO's multi-site replication is harder than just using a ready-made managed S3.
- You have no resources to track updates, disk space, and backups for MinIO itself — a managed service removes that burden entirely.
- The data volume needs to grow without a physical disk upgrade — MinIO on a VPS is limited by the server's actual disk, while managed storage scales on its own.
- You need a CDN in front of the objects with an edge network spanning the globe — Cloudflare R2 and AWS S3 integrate with a CDN natively, while MinIO needs a separate CDN setup.
- Compliance requires official certification of the storage provider (SOC2, ISO 27001) — a self-hosted MinIO does not pass that certification automatically; the responsibility is on you.
Verifying the Result
# Cluster and drive status
mc admin info myminio
# A full cycle: upload, read, delete a test object
echo "test" > test.txt
mc cp test.txt myminio/website-uploads/test.txt
mc cat myminio/website-uploads/test.txt
mc rm myminio/website-uploads/test.txt
# Public access through nginx works from the outside
curl -I https://s3.yourdomain.com/website-uploads/photo.jpgIf mc admin info shows a drive as offline, check the container logs with docker logs minio — the cause is usually the same directory permissions problem. For regular backups of MinIO itself, it makes sense to use rclone with a separate remote storage location rather than relying only on a single server's disk.