Set up MinIO as a distributed S3-compatible object storage cluster with SSL/TLS encryption and production-grade security. This tutorial covers installation, clustering, access policies, and monitoring for high-availability storage infrastructure.
Prerequisites
- Root or sudo access
- At least 4GB RAM per node
- Minimum 2 CPU cores
- Dedicated storage disks recommended
What this solves
MinIO provides S3-compatible object storage for applications needing scalable file storage, backup systems, or data lakes. This tutorial sets up a production-grade MinIO cluster with SSL/TLS encryption, distributed storage across multiple nodes, and proper access controls for enterprise workloads.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest security patches and dependencies.
sudo apt update && sudo apt upgrade -y
sudo apt install -y wget curl unzip
Create MinIO user and directories
Create a dedicated system user for MinIO and set up the required directory structure with proper permissions.
sudo useradd -r -s /bin/false minio-user
sudo mkdir -p /opt/minio/bin
sudo mkdir -p /opt/minio/data
sudo mkdir -p /etc/minio
sudo chown minio-user:minio-user /opt/minio/data
sudo chmod 755 /opt/minio/data
Download and install MinIO server
Download the latest MinIO server binary and MinIO client (mc) for administration tasks.
cd /tmp
wget https://dl.min.io/server/minio/release/linux-amd64/minio
wget https://dl.min.io/client/mc/release/linux-amd64/mc
sudo cp minio /opt/minio/bin/
sudo cp mc /usr/local/bin/
sudo chmod +x /opt/minio/bin/minio
sudo chmod +x /usr/local/bin/mc
sudo chown minio-user:minio-user /opt/minio/bin/minio
Generate SSL certificates
Create SSL certificates for secure HTTPS communication. Use Let's Encrypt for production or generate self-signed certificates for testing.
sudo mkdir -p /opt/minio/certs
sudo openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \
-subj "/C=US/ST=State/L=City/O=Organization/CN=minio.example.com" \
-keyout /tmp/private.key -out /tmp/public.crt
sudo mv /tmp/private.key /opt/minio/certs/
sudo mv /tmp/public.crt /opt/minio/certs/
sudo chown -R minio-user:minio-user /opt/minio/certs
sudo chmod 600 /opt/minio/certs/private.key
sudo chmod 644 /opt/minio/certs/public.crt
Configure MinIO environment
Create the MinIO configuration file with access credentials and server settings. Use strong passwords for production deployments.
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=StrongPassword123!
MINIO_VOLUMES="https://minio-{1...4}.example.com/opt/minio/data"
MINIO_SERVER_URL="https://minio.example.com:9000"
MINIO_BROWSER_REDIRECT_URL="https://minio-console.example.com:9001"
MINIO_OPTS="--certs-dir /opt/minio/certs --console-address :9001"
sudo chown minio-user:minio-user /etc/minio/minio.conf
sudo chmod 600 /etc/minio/minio.conf
Create systemd service
Set up MinIO as a systemd service for automatic startup and process management.
[Unit]
Description=MinIO Object Storage Server
Documentation=https://docs.min.io
Wants=network-online.target
After=network-online.target
[Service]
Type=notify
User=minio-user
Group=minio-user
EnvironmentFile=-/etc/minio/minio.conf
ExecStart=/opt/minio/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
Restart=always
LimitNOFILE=65536
TimeoutStopSec=infinity
SendSIGKILL=no
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable minio
Configure firewall rules
Open the required ports for MinIO server (9000) and web console (9001) communication.
sudo ufw allow 9000/tcp comment 'MinIO Server'
sudo ufw allow 9001/tcp comment 'MinIO Console'
sudo ufw reload
Set up distributed clustering
Configure multiple MinIO nodes for high availability. Repeat the installation on each node, then update the cluster configuration.
203.0.113.10 minio-1.example.com
203.0.113.11 minio-2.example.com
203.0.113.12 minio-3.example.com
203.0.113.13 minio-4.example.com
sudo systemctl start minio
sudo systemctl status minio
Configure MinIO client
Set up the MinIO client (mc) to manage your cluster, create buckets, and configure policies.
mc alias set mycluster https://minio.example.com:9000 minioadmin StrongPassword123! --insecure
mc admin info mycluster
mc mb mycluster/test-bucket
mc ls mycluster
Create access policies and users
Set up granular access controls with custom policies for different user roles and applications.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::test-bucket/*",
"arn:aws:s3:::test-bucket"
]
}
]
}
mc admin policy create mycluster readonly-policy /tmp/readonly-policy.json
mc admin user add mycluster testuser TestPassword123!
mc admin policy attach mycluster readonly-policy --user testuser
mc admin user list mycluster
Configure load balancer
Set up a load balancer to distribute traffic across your MinIO cluster nodes for high availability.
upstream minio_servers {
server minio-1.example.com:9000;
server minio-2.example.com:9000;
server minio-3.example.com:9000;
server minio-4.example.com:9000;
}
server {
listen 443 ssl http2;
server_name minio.example.com;
ssl_certificate /opt/minio/certs/public.crt;
ssl_certificate_key /opt/minio/certs/private.key;
client_max_body_size 1000M;
location / {
proxy_pass http://minio_servers;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
}
}
For more advanced load balancing options, check out our HAProxy tutorial.
Set up monitoring and alerts
Configure MinIO metrics export for monitoring with Prometheus and Grafana dashboards.
mc admin config set mycluster api requests_max=1600 requests_deadline=10s
mc admin config set mycluster heal bitrotscan=on max_sleep=1s max_io=10
mc admin prometheus generate mycluster
mc admin prometheus metrics mycluster
For comprehensive monitoring setup, see our Grafana and Prometheus guide.
Verify your setup
Test your MinIO installation with basic operations and cluster health checks.
sudo systemctl status minio
mc admin info mycluster
mc ls mycluster
echo "Hello MinIO" | mc pipe mycluster/test-bucket/test.txt
mc cat mycluster/test-bucket/test.txt
mc admin heal mycluster --recursive --dry-run
curl -k https://minio.example.com:9000/minio/health/live
Performance optimization
Tune system parameters
Optimize kernel parameters and file system settings for better MinIO performance.
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 65536 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
vm.swappiness = 1
vm.dirty_background_ratio = 1
vm.dirty_ratio = 5
sudo sysctl -p /etc/sysctl.d/99-minio.conf
Configure disk optimization
Set up XFS file system with optimal mount options for object storage workloads.
sudo mkfs.xfs -f /dev/sdb
sudo mkdir -p /mnt/minio-data
echo '/dev/sdb /mnt/minio-data xfs defaults,noatime,nodiratime,largeio,inode64 0 2' | sudo tee -a /etc/fstab
sudo mount -a
Backup and maintenance
Set up automated backups
Configure regular backups using MinIO's built-in replication or external backup tools.
mc alias set backup-site https://backup.example.com:9000 backupuser backuppass
mc mirror mycluster/important-bucket backup-site/important-bucket --overwrite --remove
mc admin config set mycluster scanner speed=slowest
Schedule maintenance tasks
Create cron jobs for regular cluster health checks and cleanup operations.
# MinIO health check every hour
0 minio-user /usr/local/bin/mc admin heal mycluster --recursive --dry-run > /var/log/minio-heal.log 2>&1
Daily backup at 2 AM
0 2 * minio-user /usr/local/bin/mc mirror mycluster/backups backup-site/daily/$(date +\%Y-\%m-\%d) --overwrite
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Service fails to start | Permission issues on data directory | sudo chown -R minio-user:minio-user /opt/minio/data |
| SSL certificate errors | Incorrect certificate paths or permissions | Check cert files exist and chmod 600 private.key |
| Cluster nodes can't connect | Firewall blocking inter-node communication | Allow port 9000 between all cluster nodes |
| High memory usage | Default cache settings too aggressive | Set MINIO_CACHE_DRIVES and tune cache size |
| Slow upload performance | Network or disk I/O bottlenecks | Check disk performance with iostat and network with iperf3 |
| Browser console not accessible | Console port not configured or blocked | Verify port 9001 is open and --console-address :9001 is set |
Next steps
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Default values
MINIO_DOMAIN="${1:-minio.example.com}"
CLUSTER_NODES="${2:-1}"
# Usage message
usage() {
echo "Usage: $0 [domain] [cluster_nodes]"
echo "Example: $0 minio.company.com 4"
exit 1
}
# Logging functions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Cleanup on failure
cleanup() {
log_error "Installation failed. Cleaning up..."
systemctl stop minio 2>/dev/null || true
systemctl disable minio 2>/dev/null || true
rm -f /etc/systemd/system/minio.service
userdel minio-user 2>/dev/null || true
rm -rf /opt/minio /etc/minio
}
trap cleanup ERR
# Validate arguments
if [[ $# -gt 2 ]]; then
usage
fi
if [[ ! "$CLUSTER_NODES" =~ ^[1-9][0-9]*$ ]] || [ "$CLUSTER_NODES" -lt 1 ] || [ "$CLUSTER_NODES" -gt 32 ]; then
log_error "Cluster nodes must be between 1 and 32"
exit 1
fi
# Check prerequisites
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root"
exit 1
fi
log_info "[1/10] Detecting distribution and package manager..."
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
PKG_UPGRADE="apt upgrade -y"
FIREWALL_CMD="ufw"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf check-update || true"
PKG_INSTALL="dnf install -y"
PKG_UPGRADE="dnf upgrade -y"
FIREWALL_CMD="firewall-cmd"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf check-update || true"
PKG_INSTALL="dnf install -y"
PKG_UPGRADE="dnf upgrade -y"
FIREWALL_CMD="firewall-cmd"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum check-update || true"
PKG_INSTALL="yum install -y"
PKG_UPGRADE="yum upgrade -y"
FIREWALL_CMD="firewall-cmd"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
log_info "Detected: $PRETTY_NAME using $PKG_MGR"
else
log_error "Cannot detect distribution. /etc/os-release not found."
exit 1
fi
log_info "[2/10] Updating system packages..."
$PKG_UPDATE
$PKG_UPGRADE
$PKG_INSTALL wget curl unzip openssl
log_info "[3/10] Creating MinIO user and directories..."
if ! id minio-user &>/dev/null; then
useradd -r -s /bin/false minio-user
fi
mkdir -p /opt/minio/{bin,data} /etc/minio
chown minio-user:minio-user /opt/minio/data
chmod 755 /opt/minio/data
chmod 750 /etc/minio
log_info "[4/10] Downloading and installing MinIO server..."
cd /tmp
wget -q https://dl.min.io/server/minio/release/linux-amd64/minio
wget -q https://dl.min.io/client/mc/release/linux-amd64/mc
cp minio /opt/minio/bin/
cp mc /usr/local/bin/
chmod 755 /opt/minio/bin/minio /usr/local/bin/mc
chown minio-user:minio-user /opt/minio/bin/minio
log_info "[5/10] Generating SSL certificates..."
mkdir -p /opt/minio/certs
openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \
-subj "/C=US/ST=State/L=City/O=Organization/CN=$MINIO_DOMAIN" \
-keyout /opt/minio/certs/private.key \
-out /opt/minio/certs/public.crt 2>/dev/null
chown -R minio-user:minio-user /opt/minio/certs
chmod 600 /opt/minio/certs/private.key
chmod 644 /opt/minio/certs/public.crt
log_info "[6/10] Configuring MinIO environment..."
MINIO_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-20)
# Generate cluster volumes configuration
if [ "$CLUSTER_NODES" -gt 1 ]; then
MINIO_VOLUMES="https://$MINIO_DOMAIN-{1...$CLUSTER_NODES}/opt/minio/data"
else
MINIO_VOLUMES="/opt/minio/data"
fi
cat > /etc/minio/minio.conf << EOF
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=$MINIO_PASSWORD
MINIO_VOLUMES="$MINIO_VOLUMES"
MINIO_SERVER_URL="https://$MINIO_DOMAIN:9000"
MINIO_BROWSER_REDIRECT_URL="https://$MINIO_DOMAIN:9001"
MINIO_OPTS="--certs-dir /opt/minio/certs --console-address :9001"
EOF
chown minio-user:minio-user /etc/minio/minio.conf
chmod 600 /etc/minio/minio.conf
log_info "[7/10] Creating systemd service..."
cat > /etc/systemd/system/minio.service << 'EOF'
[Unit]
Description=MinIO Object Storage Server
Documentation=https://docs.min.io
Wants=network-online.target
After=network-online.target
[Service]
Type=notify
User=minio-user
Group=minio-user
EnvironmentFile=-/etc/minio/minio.conf
ExecStart=/opt/minio/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
Restart=always
LimitNOFILE=65536
TimeoutStopSec=infinity
SendSIGKILL=no
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable minio
log_info "[8/10] Configuring firewall..."
case "$FIREWALL_CMD" in
ufw)
if command -v ufw >/dev/null 2>&1; then
ufw --force enable 2>/dev/null || true
ufw allow 9000/tcp comment 'MinIO Server' || true
ufw allow 9001/tcp comment 'MinIO Console' || true
ufw reload || true
fi
;;
firewall-cmd)
if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active firewalld >/dev/null 2>&1; then
firewall-cmd --permanent --add-port=9000/tcp || true
firewall-cmd --permanent --add-port=9001/tcp || true
firewall-cmd --reload || true
fi
;;
esac
log_info "[9/10] Starting MinIO service..."
systemctl start minio
# Wait for service to be ready
sleep 5
log_info "[10/10] Verifying installation..."
if systemctl is-active minio >/dev/null 2>&1; then
log_info "MinIO service is running"
else
log_error "MinIO service failed to start"
systemctl status minio
exit 1
fi
# Test MinIO server response
if curl -k -s https://localhost:9000/minio/health/live >/dev/null 2>&1; then
log_info "MinIO health check passed"
else
log_warn "MinIO health check failed - service may still be starting"
fi
log_info "Installation completed successfully!"
echo ""
log_info "MinIO Configuration:"
echo " Server URL: https://$MINIO_DOMAIN:9000"
echo " Console URL: https://$MINIO_DOMAIN:9001"
echo " Username: minioadmin"
echo " Password: $MINIO_PASSWORD"
echo ""
if [ "$CLUSTER_NODES" -gt 1 ]; then
log_warn "Cluster setup detected ($CLUSTER_NODES nodes):"
echo " 1. Repeat this installation on all cluster nodes"
echo " 2. Ensure DNS/hosts entries for $MINIO_DOMAIN-{1...$CLUSTER_NODES}"
echo " 3. Use the same MINIO_ROOT_USER and MINIO_ROOT_PASSWORD on all nodes"
echo " 4. Start all nodes simultaneously for initial cluster formation"
fi
log_warn "Important notes:"
echo " - Self-signed SSL certificate generated (replace with CA-signed for production)"
echo " - Save the password in a secure location"
echo " - Configure your application to use the MinIO S3-compatible API"
Review the script before running. Execute with: bash install.sh