MinIO: Self-Hosted S3-Compatible Object Storage
Run your own S3-compatible storage. Perfect for backups, static sites, and cloud-native apps.
Deploy your own high-performance, S3-compatible object storage with MinIO. This comprehensive guide shows you how to build a robust, scalable storage backend for backups, static sites, application data, and more - all within your homelab.
Difficulty: Intermediate Estimated Time: 45 minutes Last Updated: December 2025 Target Audience: Homelab administrators, developers, backup enthusiasts
What You’ll Learn
- Deploy MinIO for S3-compatible object storage
- Configure buckets and access policies
- Use the MinIO Client (mc) CLI tool
- Integrate with backup solutions (Restic, Borg, Duplicati)
- Set up lifecycle policies for automatic data management
- Configure versioning and object locking
- Implement high availability with erasure coding
- Monitor storage metrics and health
- Secure MinIO with TLS and access controls
Prerequisites
Required Knowledge
- Docker and Docker Compose fundamentals
- Basic understanding of object storage concepts
- Linux command-line proficiency
- Networking basics (ports, DNS, reverse proxy)
Required Infrastructure
- Linux server or NAS (Ubuntu 22.04+, Debian 11+)
- Minimum: 2 GB RAM, 2 CPU cores, 20 GB storage
- Recommended: 8+ GB RAM, 4+ cores, dedicated storage drives
- Docker and Docker Compose installed
- Adequate storage space for object data
Pre-Deployment Checklist
- Storage location planned (dedicated drives recommended)
- Network configuration verified
- Access credentials generated
- Reverse proxy configured (for HTTPS)
- Backup destinations identified
1. Understanding MinIO
What is MinIO?
MinIO is a high-performance, S3-compatible object storage system designed for cloud-native applications. It’s open-source, lightweight, and perfect for self-hosted environments where you need scalable storage without AWS costs.
Key Features:
- S3 Compatible: Works with any S3-compatible application
- High Performance: Optimized for modern hardware (NVMe, SSDs)
- Distributed: Scale from single-node to petabyte-scale clusters
- Erasure Coding: Data protection without RAID overhead
- Object Versioning: Keep multiple versions of objects
- Lifecycle Policies: Automatic data expiration and tiering
- Encryption: Server-side and client-side encryption support
- Multi-Tenancy: Separate buckets with access controls
- Web Console: Beautiful, intuitive management interface
Why Use MinIO in Your Homelab?
Common Use Cases:
- Backup Backend: Store Restic, Borg, Duplicati, or Veeam backups
- Static Site Hosting: Serve static websites and media
- Application Storage: Backend for apps needing object storage
- Media Archives: Store photos, videos, and documents
- CI/CD Artifacts: Store build artifacts and container images
- Logging and Monitoring: Store logs, metrics, and traces
- Data Lake: Centralized storage for data analytics
MinIO vs. Alternatives
| Feature | MinIO | AWS S3 | Ceph | Nextcloud |
|---|---|---|---|---|
| S3 Compatible | Yes | Yes (original) | Yes | No |
| Self-Hosted | Yes | No | Yes | Yes |
| Setup Complexity | Easy | N/A | Complex | Easy |
| Performance | Excellent | Excellent | Good | Moderate |
| Scalability | Excellent | Unlimited | Excellent | Limited |
| Cost | Free | Pay-per-use | Free | Free |
| Web Interface | Excellent | Good | Basic | Excellent |
| Use Case | Object storage | Cloud storage | All-purpose | File sync/share |
How MinIO Works
Architecture:
Clients (Restic, Apps, mc CLI)
↓
MinIO Server
↓
Object Storage
↓
Buckets → Objects (files) Bucket and Object Model:
MinIO Storage
├── bucket-backups/
│ ├── restic/
│ │ ├── config
│ │ ├── data/
│ │ └── keys/
│ └── borg/
├── bucket-media/
│ ├── photos/
│ └── videos/
└── bucket-static-sites/
└── website/
├── index.html
└── assets/ Erasure Coding:
MinIO splits objects into data and parity chunks for redundancy:
Original Object (10 MB)
↓
Split into 4 data chunks + 2 parity chunks
↓
Can lose any 2 chunks and still recover data
↓
Storage efficiency: Better than RAID 6 2. Installing MinIO
Single-Node Docker Deployment
Create directory structure:
# Create MinIO directories
mkdir -p /opt/minio/{data,config}
cd /opt/minio Create docker-compose.yml:
version: '3.8'
services:
minio:
image: minio/minio:latest
container_name: minio
hostname: minio
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
# Root credentials (change these!)
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: changeme-secure-password
# Optional: Enable browser access
MINIO_BROWSER: "on"
# Optional: Domain configuration
MINIO_DOMAIN: minio.yourhomelab.local
MINIO_SERVER_URL: https://s3.yourhomelab.local
ports:
# S3 API endpoint
- "9000:9000"
# Web console
- "9001:9001"
volumes:
# Object data storage
- /mnt/storage/minio-data:/data
# Configuration
- ./config:/root/.minio
networks:
- minio-network
# Resource limits
deploy:
resources:
limits:
cpus: '2'
memory: 4G
reservations:
cpus: '1'
memory: 2G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
networks:
minio-network:
driver: bridge Secure the configuration:
# Create strong credentials
MINIO_ROOT_USER="admin"
MINIO_ROOT_PASSWORD=$(openssl rand -base64 32)
# Save to .env file
cat > .env <<EOF
MINIO_ROOT_USER=$MINIO_ROOT_USER
MINIO_ROOT_PASSWORD=$MINIO_ROOT_PASSWORD
EOF
# Protect .env file
chmod 600 .env
# Update docker-compose.yml to use env_file Updated docker-compose.yml:
services:
minio:
# ... (previous config)
env_file:
- .env
environment:
MINIO_BROWSER: "on" Deploy MinIO:
# Start MinIO
docker-compose up -d
# View logs
docker-compose logs -f minio
# Verify status
docker-compose ps
curl http://localhost:9000/minio/health/live Access the web console:
Open http://your-server-ip:9001 and login with your credentials.
Multi-Drive Deployment (Better Performance)
For homelabs with multiple drives, use distributed mode:
version: '3.8'
services:
minio:
image: minio/minio:latest
container_name: minio
restart: unless-stopped
# Use multiple drives for erasure coding
command: server /data{1...4} --console-address ":9001"
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: your-secure-password
ports:
- "9000:9000"
- "9001:9001"
volumes:
# Map to 4 different drives
- /mnt/disk1:/data1
- /mnt/disk2:/data2
- /mnt/disk3:/data3
- /mnt/disk4:/data4
networks:
- minio-network This provides erasure coding across drives, allowing one drive failure without data loss.
Native Installation (No Docker)
Download and install:
# Download MinIO server
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/
# Verify
minio --version Create systemd service:
sudo nano /etc/systemd/system/minio.service Add:
[Unit]
Description=MinIO Object Storage
Documentation=https://min.io/docs
After=network.target
[Service]
Type=notify
User=minio
Group=minio
Environment="MINIO_ROOT_USER=admin"
Environment="MINIO_ROOT_PASSWORD=your-secure-password"
ExecStart=/usr/local/bin/minio server /mnt/storage/minio-data --console-address ":9001"
Restart=always
LimitNOFILE=65536
TasksMax=infinity
TimeoutStopSec=infinity
SendSIGKILL=no
[Install]
WantedBy=multi-user.target Create user and directories:
# Create minio user
sudo useradd -r -s /sbin/nologin minio
# Create data directory
sudo mkdir -p /mnt/storage/minio-data
sudo chown -R minio:minio /mnt/storage/minio-data
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable minio.service
sudo systemctl start minio.service
sudo systemctl status minio.service 3. Initial Configuration
First Login and Setup
Access web console:
- Navigate to
http://your-server-ip:9001 - Login with root credentials
- You’ll see the MinIO dashboard
Creating Access Keys
For applications and CLI tools, create access keys instead of using root credentials.
Via Web Console:
- Click Identity → Users
- Click Create User
- Enter username (e.g.,
backup-user) - Generate password
- Click Create
- Assign policies (e.g.,
readwriteor custom) - Note the Access Key and Secret Key
Via mc CLI:
# We'll install mc CLI in the next section
mc admin user add myminio backup-user SecurePassword123
mc admin policy attach myminio readwrite --user backup-user Creating Your First Bucket
Via Web Console:
- Click Buckets → Create Bucket
- Enter bucket name (e.g.,
backups) - Configure settings:
- Versioning: Enable/Disable
- Object Locking: Enable for immutable backups
- Quota: Set size limit (optional)
- Click Create Bucket
Via mc CLI:
mc mb myminio/backups 4. MinIO Client (mc) Setup
Installing mc CLI
Linux:
# Download
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/
# Verify
mc --version macOS:
brew install minio/stable/mc Docker:
# Run as container
docker run -it --rm minio/mc --help Configuring mc
Add MinIO server:
# Syntax: mc alias set <ALIAS> <URL> <ACCESS_KEY> <SECRET_KEY>
mc alias set myminio http://192.168.1.100:9000 admin your-password
# Verify connection
mc admin info myminio For HTTPS:
mc alias set myminio https://s3.yourhomelab.local admin your-password List configured aliases:
mc alias list Basic mc Commands
Bucket operations:
# List buckets
mc ls myminio
# Create bucket
mc mb myminio/my-new-bucket
# Remove empty bucket
mc rb myminio/my-new-bucket
# Remove bucket with contents (use with caution!)
mc rb --force myminio/my-new-bucket Object operations:
# Upload file
mc cp /path/to/file.txt myminio/backups/
# Upload directory
mc cp --recursive /path/to/directory/ myminio/backups/
# Download file
mc cp myminio/backups/file.txt /path/to/destination/
# List objects
mc ls myminio/backups/
# Remove object
mc rm myminio/backups/file.txt
# Remove all objects in path
mc rm --recursive --force myminio/backups/old-files/ Sync operations:
# Sync local directory to bucket (one-way)
mc mirror /local/path/ myminio/backups/
# Sync bucket to local (download)
mc mirror myminio/backups/ /local/path/
# Watch for changes and sync continuously
mc mirror --watch /local/path/ myminio/backups/ Advanced operations:
# Find files
mc find myminio/backups --name "*.jpg"
# Find files larger than 100MB
mc find myminio/backups --larger 100MB
# Copy between buckets
mc cp myminio/source-bucket/file.txt myminio/dest-bucket/
# Diff between local and bucket
mc diff /local/path/ myminio/backups/ 5. Integration with Backup Tools
Restic Integration
Configure Restic to use MinIO:
# Set environment variables
export RESTIC_REPOSITORY="s3:http://192.168.1.100:9000/backups"
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export RESTIC_PASSWORD="your-restic-password"
# Initialize repository
restic init
# Create backup
restic backup /data
# List snapshots
restic snapshots For HTTPS (with custom CA):
export RESTIC_REPOSITORY="s3:https://s3.yourhomelab.local/backups"
export AWS_CA_BUNDLE="/path/to/ca-cert.pem" Duplicati Integration
Configure Duplicati to use MinIO:
- Create new backup
- Storage Type: S3 Compatible
- Settings:
Server: s3.yourhomelab.local:9000 Bucket name: backups Folder path: duplicati/ AWS Access ID: your-access-key AWS Secret Key: your-secret-key Use SSL: ☐ (if using HTTP) or ☑ (if HTTPS) - Test connection
- Continue with backup configuration
Borg Integration (via rclone)
Borg doesn’t natively support S3, but can use rclone as a backend:
Install rclone:
sudo apt install rclone Configure rclone for MinIO:
rclone config
# Choose: n (New remote)
# Name: minio
# Storage: s3 (Amazon S3)
# Provider: Minio
# Access Key: your-access-key
# Secret Key: your-secret-key
# Endpoint: http://192.168.1.100:9000
# Location constraint: (leave empty)
# ACL: (leave default) Mount MinIO via rclone:
# Mount bucket as filesystem
mkdir -p /mnt/minio-backups
rclone mount minio:backups /mnt/minio-backups --daemon
# Now use with Borg
borg init /mnt/minio-backups/borg-repo
borg create /mnt/minio-backups/borg-repo::archive /data Veeam Integration
Veeam supports S3 natively:
- Add Repository → Object Storage
- Type: S3 Compatible
- Settings:
Service Point: http://192.168.1.100:9000 Region: us-east-1 (or any) Access Key: your-access-key Secret Key: your-secret-key Bucket: veeam-backups Folder: backups/ - Test connection
- Save
6. Access Policies and Security
Understanding MinIO Policies
MinIO uses AWS IAM-compatible policies for access control.
Built-in policies:
readonly: Read-only access to all bucketsreadwrite: Read/write access to all bucketswriteonly: Upload-only, no readdiagnostics: Admin diagnostics accessconsoleAdmin: Full admin access via console
Assign policy to user:
mc admin policy attach myminio readwrite --user backup-user Creating Custom Policies
Example: Bucket-specific access:
Create policy file backup-only.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::backups/*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::backups"
]
}
]
} Apply policy:
mc admin policy create myminio backup-only backup-only.json
mc admin policy attach myminio backup-only --user backup-user Example: Read-only public bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": ["*"]
},
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::public-files/*"
]
}
]
} Apply to bucket:
mc anonymous set download myminio/public-files Bucket-Level Policies
Set bucket policy via mc:
# Make bucket publicly readable
mc anonymous set public myminio/public-files
# Make bucket downloadable (not listable)
mc anonymous set download myminio/downloads
# Make bucket uploadable
mc anonymous set upload myminio/uploads
# Remove public access
mc anonymous set none myminio/private-bucket Enabling HTTPS/TLS
Generate certificates:
# Self-signed certificate (for testing)
mkdir -p /opt/minio/certs
openssl req -x509 -newkey rsa:4096 -keyout /opt/minio/certs/private.key -out /opt/minio/certs/public.crt -days 365 -nodes -subj "/CN=s3.yourhomelab.local" Configure MinIO (Docker):
services:
minio:
# ... (previous config)
volumes:
- ./certs:/root/.minio/certs
environment:
MINIO_SERVER_URL: https://s3.yourhomelab.local:9000 Or for native installation:
sudo mkdir -p /home/minio/.minio/certs
sudo cp public.crt /home/minio/.minio/certs/
sudo cp private.key /home/minio/.minio/certs/
sudo chown -R minio:minio /home/minio/.minio/certs
sudo chmod 600 /home/minio/.minio/certs/private.key Restart MinIO:
sudo systemctl restart minio Use Let’s Encrypt with reverse proxy (recommended):
Use Nginx Proxy Manager or Caddy to provide HTTPS:
Caddy example:
s3.yourhomelab.local {
reverse_proxy localhost:9000
tls [email protected]
}
console.yourhomelab.local {
reverse_proxy localhost:9001
tls [email protected]
} 7. Versioning and Object Locking
Object Versioning
Versioning keeps multiple versions of objects, protecting against accidental deletion or modification.
Enable versioning on bucket:
# Via mc CLI
mc version enable myminio/backups
# Verify
mc version info myminio/backups Via Web Console:
- Go to bucket
- Click Settings
- Enable Versioning
List object versions:
mc ls --versions myminio/backups/file.txt Restore previous version:
# Download specific version
mc cp --version-id=VERSION_ID myminio/backups/file.txt ./restored-file.txt Delete specific version:
mc rm --version-id=VERSION_ID myminio/backups/file.txt Object Locking (Immutability)
Protect objects from deletion or modification for compliance/ransomware protection.
Enable object locking (must be set at bucket creation):
mc mb --with-lock myminio/immutable-backups Set retention policy:
# Retention mode: GOVERNANCE or COMPLIANCE
# GOVERNANCE: Can be removed by users with permission
# COMPLIANCE: Cannot be removed until retention period expires
mc retention set --default GOVERNANCE "30d" myminio/immutable-backups Set legal hold:
# Legal hold: Object cannot be deleted regardless of retention
mc legalhold set myminio/immutable-backups/important-file.txt
# Remove legal hold
mc legalhold clear myminio/immutable-backups/important-file.txt 8. Lifecycle Management
Automatic Object Expiration
Configure rules to automatically delete or transition objects.
Example lifecycle policy:
Create lifecycle.json:
{
"Rules": [
{
"ID": "DeleteOldBackups",
"Status": "Enabled",
"Filter": {
"Prefix": "daily-backups/"
},
"Expiration": {
"Days": 30
}
},
{
"ID": "DeleteIncompleteUploads",
"Status": "Enabled",
"Filter": {},
"AbortIncompleteMultipartUpload": {
"DaysAfterInitiation": 7
}
},
{
"ID": "ExpireOldVersions",
"Status": "Enabled",
"Filter": {},
"NoncurrentVersionExpiration": {
"NoncurrentDays": 90
}
}
]
} Apply lifecycle policy:
mc ilm import myminio/backups < lifecycle.json
# Verify
mc ilm ls myminio/backups Remove lifecycle rules:
mc ilm rm --id="DeleteOldBackups" myminio/backups 9. Monitoring and Performance
MinIO Metrics
View server info:
mc admin info myminio Monitor server usage:
# Real-time stats
mc admin top api myminio
mc admin top locks myminio
mc admin top disk myminio Prometheus Integration
MinIO exports Prometheus metrics by default.
Metrics endpoint:
http://your-server:9000/minio/v2/metrics/cluster Prometheus scrape config:
# prometheus.yml
scrape_configs:
- job_name: 'minio'
metrics_path: /minio/v2/metrics/cluster
scheme: http
static_configs:
- targets: ['192.168.1.100:9000']
basic_auth:
username: 'prometheus'
password: 'your-prometheus-password' Create Prometheus user in MinIO:
mc admin user add myminio prometheus SecurePrometheusPassword
mc admin policy attach myminio diagnostics --user prometheus Grafana Dashboard
Import MinIO dashboard:
- Grafana → Dashboards → Import
- Dashboard ID:
13502(Official MinIO Dashboard) - Select Prometheus data source
- Import
Metrics include:
- Total storage usage
- Request rates (GET, PUT, DELETE)
- Error rates
- Bandwidth utilization
- Bucket statistics
Health Checks
# Liveness check (is MinIO running?)
curl http://localhost:9000/minio/health/live
# Readiness check (is MinIO ready for requests?)
curl http://localhost:9000/minio/health/ready
# Cluster health
mc admin heal myminio 10. Backup and Disaster Recovery
Backing Up MinIO Configuration
Export configuration:
# Export all config
mc admin config export myminio > minio-config-backup.txt
# Save to secure location
gpg --encrypt --recipient [email protected] minio-config-backup.txt Export policies and users:
# List and backup all users
mc admin user list myminio > users-backup.txt
# List all policies
mc admin policy list myminio > policies-backup.txt Backing Up Data
Option 1: Mirror to another MinIO instance:
# Configure second MinIO instance
mc alias set minio-backup https://backup-server:9000 admin backup-password
# Continuous mirroring
mc mirror --watch myminio/backups minio-backup/backups-mirror Option 2: Backup to filesystem:
# Sync bucket to local filesystem
mc mirror myminio/critical-data /mnt/backup/minio-mirror/
# Schedule with cron
0 2 * * * mc mirror myminio/critical-data /mnt/backup/minio-mirror/ Option 3: Use Restic to backup MinIO data directory:
# Backup MinIO data directory
restic backup /mnt/storage/minio-data --exclude="*.tmp" --tag minio-backup Disaster Recovery
Full recovery procedure:
- Restore MinIO configuration:
# Reinstall MinIO
docker-compose up -d minio
# Import configuration
mc admin config import myminio < minio-config-backup.txt
mc admin service restart myminio - Restore data:
# Option A: From mirror
mc mirror minio-backup/backups-mirror myminio/backups
# Option B: From filesystem backup
mc mirror /mnt/backup/minio-mirror/ myminio/critical-data - Verify integrity:
mc admin heal --recursive myminio
mc ls --recursive myminio/backups Conclusion
You now have a comprehensive MinIO deployment providing S3-compatible object storage for your homelab.
Key Takeaways:
- S3 Compatibility: Works with any S3-compatible tool or application
- Performance: High-throughput storage for modern workloads
- Flexibility: Single-node to distributed multi-drive setups
- Security: Encryption, access policies, and immutable backups
- Cost-Effective: No cloud storage fees, full control
Your Next Steps:
- Integrate with backup solutions (Restic, Duplicati, Veeam)
- Set up lifecycle policies for automatic management
- Enable versioning for critical buckets
- Configure monitoring and alerting
- Document your bucket structure and policies
- Test disaster recovery procedures
Related Guides:
- Restic + Backrest: Modern Backup Strategy for Homelabs
- Borg Backup + Vorta: Efficient Homelab Backups
- Syncthing: Peer-to-Peer File Synchronization
Your homelab now has enterprise-grade object storage!
Guide Version: 1.0 Last Updated: December 2025 Estimated Read Time: 30-40 minutes Estimated Implementation Time: 45 minutes
Created for homelabbing.io - AI-Forward Homelab Resource Platform