Self-Hosted MinIO S3 Object Storage on Linux
- Author :Liam K.
- Date :June 30, 2026
- Time :18 minutes
MinIO provides an S3-compatible API for object storage on your own infrastructure. It is widely used for backups, media assets, CI artifacts, and application uploads — without vendor lock-in to a single cloud provider. This guide covers a single-node production baseline you can extend to distributed mode later.
Prerequisites
- Ubuntu 22.04+ or Debian 12 with a dedicated data volume (e.g.
/mnt/minio-data) - DNS record for
s3.example.compointing to the server - At least 4 GB RAM and fast SSD storage for metadata-heavy workloads
Step 1: Create Dedicated User and Data Directory
sudo useradd -r -s /sbin/nologin minio-user || true
sudo mkdir -p /mnt/minio-data
sudo chown -R minio-user:minio-user /mnt/minio-data
sudo chmod 750 /mnt/minio-dataStep 2: Download and Install MinIO Binary
cd /tmp
curl -LO https://dl.min.io/server/minio/release/linux-amd64/minio
sudo install -m 755 minio /usr/local/bin/minio
minio --versionStep 3: Install MinIO Client (mc)
curl -LO https://dl.min.io/client/mc/release/linux-amd64/mc
sudo install -m 755 mc /usr/local/bin/mc
mc --versionStep 4: Configure Environment and systemd Service
sudo tee /etc/default/minio >/dev/null <<'EOF'
MINIO_ROOT_USER="minio-admin"
MINIO_ROOT_PASSWORD="REPLACE_WITH_LONG_RANDOM_PASSWORD"
MINIO_VOLUMES="/mnt/minio-data"
MINIO_OPTS="--console-address :9001"
MINIO_SERVER_URL="https://s3.example.com"
EOF
sudo chmod 600 /etc/default/minio
[...]Step 5: Put TLS in Front with Caddy or Nginx
Terminate TLS at your reverse proxy and forward to MinIO on localhost port 9000. Example Caddy block:
s3.example.com {
reverse_proxy 127.0.0.1:9000
}
console.s3.example.com {
reverse_proxy 127.0.0.1:9001
}Step 6: Configure mc Alias and Create Buckets
mc alias set local https://s3.example.com minio-admin 'REPLACE_WITH_LONG_RANDOM_PASSWORD'
mc mb local/app-uploads
mc mb local/backups
mc anonymous set none local/app-uploads
mc anonymous set none local/backupsStep 7: Create Application IAM Policy
cat > /tmp/app-upload-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket"],
"Resource": [
[...]Step 8: Set Lifecycle Rules
cat > /tmp/lifecycle.json <<'EOF'
{
"Rules": [
{
"ID": "expire-temp-uploads",
"Status": "Enabled",
"Filter": { "Prefix": "tmp/" },
"Expiration": { "Days": 7 }
[...]Step 9: Verify with aws-cli Compatibility
export AWS_ACCESS_KEY_ID=app-uploader
export AWS_SECRET_ACCESS_KEY=APP_USER_SECRET
export AWS_DEFAULT_REGION=us-east-1
aws --endpoint-url https://s3.example.com s3 ls s3://app-uploads/
echo "health-check" | aws --endpoint-url https://s3.example.com s3 cp - s3://app-uploads/health-check.txt
aws --endpoint-url https://s3.example.com s3 rm s3://app-uploads/health-check.txtOperations Checklist
- Monitor disk usage on
/mnt/minio-data— object storage failures are often capacity failures. - Rotate root and application credentials on a defined schedule.
- Restrict console access (
:9001) to admin IPs or VPN only. - Include MinIO data in your backup and restore drill program.
- Plan erasure coding or distributed mode before you outgrow single-node I/O.
"S3-compatible storage is only production-ready when bucket policies, lifecycle rules, and restore tests are treated as part of the platform — not afterthoughts."
Technical Author

System administrator and technical writer specializing in server infrastructure, security and deployment. Creating comprehensive guides to help you master server administration.