VPS Tutorials
Deploying MinIO Object Storage on a VPS (2026): Docker, S3, HTTPS, Backups and Security Configuration
Starting from Ubuntu 24.04, deploy MinIO on a VPS with Docker Compose, configure Caddy HTTPS, S3 clients, least privilege, lifecycle rules, disk monitoring and off-site backups — suitable for personal sites and small teams needing object storage.

Deploying MinIO Object Storage on a VPS (2026): Docker, S3, HTTPS, Backups and Security Configuration
If you need S3-compatible object storage for a website, image service, backup scripts or AI applications, MinIO is a cleaner approach than piling files directly into a VPS directory. Using Ubuntu 24.04 and Docker Compose as an example, this article walks through a complete single-node MinIO setup, from deployment, HTTPS, S3 clients and lifecycle rules to backups and security hardening.
Single-node MinIO suits personal projects, small websites, media libraries and development environments; it is not equivalent to highly available object storage. We recommend at least 2 cores, 4GB RAM and 80GB+ SSD, with the data directory on an expandable volume. When images and backups grow quickly, capacity becomes the bottleneck before CPU does.
| Scenario | Recommended specs | Notes |
|---|---|---|
| Development/testing | 1 core, 2GB, 40GB SSD | Few objects; cannot serve as the only backup |
| Personal site | 2 cores, 4GB, 100GB SSD | Images, attachments, build artifacts |
| Small-team production | 4 cores, 8GB, 200GB+ SSD | Add off-site backups |
If the VPS disk frequently shows I/O spikes, first refer to the VPS disk I/O troubleshooting guide; do not stack MinIO, databases and compilation tasks on the same low-performance disk.
sudo apt update
sudo apt install -y ca-certificates curl
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"
newgrp docker
sudo mkdir -p /opt/minio/{data,config}
sudo chown -R "$USER":"$USER" /opt/minio
cd /opt/minio
Do not place the data directory under /tmp, and do not expose ports 9000 and 9001 directly to the public internet.
Create .env using credentials that are long enough and not reused from other services:
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=替换为至少32位随机密码
Create compose.yml:
services:
minio:
image: quay.io/minio/minio:latest
container_name: minio
restart: unless-stopped
env_file: .env
command: server /data --console-address ":9001"
ports:
- "127.0.0.1:9000:9000"
- "127.0.0.1:9001:9001"
volumes:
- ./data:/data
- ./config:/root/.minio
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"]
interval: 30s
timeout: 10s
retries: 3
Start and check:
docker compose up -d
docker compose ps
docker logs --tail=100 minio
Point s3.example.com and minio.example.com to the VPS, then add the following to the Caddyfile:
s3.example.com {
reverse_proxy 127.0.0.1:9000
}
minio.example.com {
reverse_proxy 127.0.0.1:9001
}
Caddy automatically obtains and renews certificates. If you don't have a unified reverse proxy entry point yet, read Configuring automatic HTTPS with Caddy on a VPS first. After changes, run:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
curl -I https://s3.example.com/minio/health/live
Use different domains for the S3 API and the Console, so the management interface and the application upload endpoint are not mixed together.
After logging in to https://minio.example.com, create the uploads bucket. Do not let applications use the root account; create a dedicated user under Access > Users and attach only the required policies:
{
"Version": "2012-10-17",
"Statement": [
{"Action": ["s3:GetBucketLocation", "s3:ListBucket"], "Effect": "Allow", "Resource": ["arn:aws:s3:::uploads"]},
{"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], "Effect": "Allow", "Resource": ["arn:aws:s3:::uploads/*"]}
]
}
On the application side, store only the API domain, Access Key, Secret Key, bucket and region (usually us-east-1). For direct frontend uploads, use short-lived presigned URLs and never put the Secret Key in browser code.
aws configure --profile minio
aws --profile minio --endpoint-url https://s3.example.com s3 ls
echo "minio test" > /tmp/minio-test.txt
aws --profile minio --endpoint-url https://s3.example.com s3 cp /tmp/minio-test.txt s3://uploads/health/minio-test.txt
aws --profile minio --endpoint-url https://s3.example.com s3 cp s3://uploads/health/minio-test.txt -
If SignatureDoesNotMatch appears, check whether the endpoint has an extra path, whether the system time is accurate, whether the reverse proxy rewrites the Host header, and whether the client has enabled virtual-hosted-style addressing.
In Bucket > Lifecycle Rules, set expiration for temporary objects, for example tmp/ for 7 days and backups/ for 30 days. Keep an eye on:
df -h /opt/minio/data
du -sh /opt/minio/data/*
docker stats minio --no-stream
When disk usage reaches 80%, expand or clean up; at 90%, do not wait any longer. The /data directory is not a backup — keep at least one off-site copy:
mc alias set local https://s3.example.com ACCESS_KEY SECRET_KEY
mc alias set backup https://backup-s3.example.net BACKUP_KEY BACKUP_SECRET
mc mirror --overwrite --remove local/uploads backup/minio-uploads
Backup scripts should record exit codes, object counts and elapsed time, and you should periodically test restores. You can continue reading the VPS backup and restore drill guide.
- Set
.envpermissions to600and never commit it to Git:chmod 600 /opt/minio/.env. - Only open SSH, 80 and 443; keep 9000/9001 listening on localhost only.
- Create a separate Access Key for each application, restricting permissions by bucket and action.
- Disable anonymous access you don't need; use read-only policies or temporary URLs for public downloads.
- Configure alerts for disk, containers, certificates and backups, and run a restore test at least once a month.
- Pin the MinIO image to a tested version; back up and read the release notes before upgrading.
Confirm the browser is accessing the Console domain while the S3 endpoint is set to the API domain; also check the bucket policy, user policy and CORS. Do not use minio.example.com as the S3 endpoint.
Check whether Caddy preserves the Host header, whether DNS points to the current VPS, and whether the system time is accurate. S3 signatures depend on the request Host and time; if the proxy layer rewrites either, authentication can fail.
Confirm Compose uses the correct mount path and run docker inspect minio to inspect Mounts. A wrong working directory can cause the container to mount a different, empty directory.
The key to single-node MinIO is not just getting the container running, but designing the API domain, management domain, least privilege, disk alerts and off-site backups together. Personal projects can start with 2 cores and 4GB; once object storage becomes the only copy of your data, add independent backups, or even migrate to a multi-node setup or a managed S3 service.