Skip to main content

MinIO on a VPS: Your Own S3 Storage in 15 Minutes

Cloud & DevOps · 19.04.2026 · 5 min read
Illustration for “MinIO on a VPS: Your Own S3 Storage in 15 Minutes”

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-stopped
mkdir -p /data/minio
docker compose up -d

Nginx 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 appuser

Integration 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'];
Tip: MinIO supports file versioning, lifecycle policies, and replication across multiple servers. Enable versioning with the command 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 permissions

The /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 authority

This 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

CriterionMinIO on your own VPSHetzner Object Storage / R2
Control over the dataFull, physically on your own diskData lives with the provider
Maintenance and updatesYour responsibilityThe provider updates and patches it
Scaling capacityLimited by the server's physical diskGrows automatically without your involvement
SOC2/ISO storage certificationEntirely your responsibilityLarge 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.jpg

If 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.

Was this article helpful?
← Back to Knowledge Base Ask Support