Set up a production-ready MinIO cluster with multiple nodes, erasure coding, and automatic failover. This tutorial covers distributed mode configuration, load balancing with SSL termination, and health monitoring for enterprise object storage.
Prerequisites
- 6 Linux servers with at least 4GB RAM each
- Dedicated storage volumes on each node
- Network connectivity between all nodes
- Basic understanding of Linux system administration
What this solves
MinIO clustering provides enterprise-grade object storage with high availability and automatic failover. This setup ensures your applications can continue accessing data even when multiple nodes fail, with erasure coding protecting against data loss and distributed architecture eliminating single points of failure.
Architecture planning and prerequisites
Plan your cluster topology
MinIO requires a minimum of 4 nodes for high availability with erasure coding. This tutorial uses 6 nodes for optimal redundancy and performance.
| Node | IP Address | Role | Storage |
|---|---|---|---|
| minio-1 | 203.0.113.10 | Primary | /data/disk{1...4} |
| minio-2 | 203.0.113.11 | Secondary | /data/disk{1...4} |
| minio-3 | 203.0.113.12 | Secondary | /data/disk{1...4} |
| minio-4 | 203.0.113.13 | Secondary | /data/disk{1...4} |
| minio-5 | 203.0.113.14 | Secondary | /data/disk{1...4} |
| minio-6 | 203.0.113.15 | Secondary | /data/disk{1...4} |
Prepare storage volumes
Each node requires dedicated storage volumes. Create four mount points per node for optimal performance and redundancy.
sudo mkdir -p /data/disk{1,2,3,4}
sudo chown minio-user:minio-user /data/disk{1,2,3,4}
sudo chmod 755 /data/disk{1,2,3,4}
Step-by-step installation
Update system packages on all nodes
Ensure all nodes have the latest security updates before installing MinIO.
sudo apt update && sudo apt upgrade -y
sudo apt install -y wget curl systemd
Download and install MinIO binary
Install the latest stable MinIO server binary on all nodes.
wget https://dl.min.io/server/minio/release/linux-amd64/minio
sudo chmod +x minio
sudo mv minio /usr/local/bin/
sudo ln -sf /usr/local/bin/minio /usr/local/bin/minio
Create MinIO user and group
Create a dedicated system user for running MinIO services securely.
sudo groupadd -r minio-user
sudo useradd -M -r -g minio-user minio-user
sudo chown minio-user:minio-user /data/disk{1,2,3,4}
Configure host resolution
Add all cluster nodes to the hosts file on each server for reliable hostname resolution.
# MinIO Cluster Nodes
203.0.113.10 minio-1
203.0.113.11 minio-2
203.0.113.12 minio-3
203.0.113.13 minio-4
203.0.113.14 minio-5
203.0.113.15 minio-6
Configure MinIO distributed mode
Create MinIO configuration directory
Set up configuration directories with proper permissions for the MinIO service.
sudo mkdir -p /etc/minio
sudo chown minio-user:minio-user /etc/minio
sudo chmod 750 /etc/minio
Generate cluster credentials
Create strong access credentials that will be shared across all cluster nodes.
openssl rand -base64 32 | tr -d "=+/" | cut -c1-20 > /tmp/access_key
openssl rand -base64 48 | tr -d "=+/" | cut -c1-40 > /tmp/secret_key
echo "Access Key: $(cat /tmp/access_key)"
echo "Secret Key: $(cat /tmp/secret_key)"
Create MinIO environment configuration
Configure environment variables for distributed mode with erasure coding enabled.
# MinIO Cluster Configuration
MINIO_ROOT_USER="$(cat /tmp/access_key)"
MINIO_ROOT_PASSWORD="$(cat /tmp/secret_key)"
MINIO_VOLUMES="http://minio-{1...6}/data/disk{1...4}"
MINIO_OPTS="--console-address :9001 --address :9000"
MINIO_SERVER_URL="https://minio-cluster.example.com:9000"
MINIO_BROWSER_REDIRECT_URL="https://minio-cluster.example.com:9001"
Security and Performance
MINIO_PROMETHEUS_AUTH_TYPE="public"
MINIO_HEAL_WORKERS="4"
MINIO_HEAL_DRIVE_WORKERS="2"
Set configuration file permissions
Secure the configuration file containing sensitive credentials.
sudo chown minio-user:minio-user /etc/minio/minio.conf
sudo chmod 640 /etc/minio/minio.conf
rm /tmp/access_key /tmp/secret_key
Create systemd service file
Configure MinIO as a systemd service for automatic startup and management.
[Unit]
Description=MinIO Object Storage Server
Documentation=https://docs.min.io
Wants=network-online.target
After=network-online.target
AssertFileIsExecutable=/usr/local/bin/minio
[Service]
WorkingDirectory=/usr/local
User=minio-user
Group=minio-user
EnvironmentFile=-/etc/minio/minio.conf
ExecStartPre=/bin/bash -c "if [ -z \"${MINIO_VOLUMES}\" ]; then echo \"Variable MINIO_VOLUMES not set in /etc/minio/minio.conf\"; exit 1; fi"
ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
Restart=always
RestartSec=10
LimitNOFILE=65536
TimeoutStopSec=infinity
SendSIGKILL=no
SuccessExitStatus=0
StandardOutput=journal
StandardError=journal
SyslogIdentifier=minio
KillMode=mixed
KillSignal=SIGTERM
[Install]
WantedBy=multi-user.target
Configure SSL and load balancing
Generate SSL certificates
Create SSL certificates for secure communication between cluster nodes and clients.
sudo mkdir -p /etc/minio/certs
sudo openssl req -new -x509 -days 365 -nodes \
-out /etc/minio/certs/public.crt \
-keyout /etc/minio/certs/private.key \
-subj "/C=US/ST=CA/L=San Francisco/O=Example/CN=minio-cluster.example.com"
sudo chown -R minio-user:minio-user /etc/minio/certs
sudo chmod 640 /etc/minio/certs/private.key
sudo chmod 644 /etc/minio/certs/public.crt
Install and configure HAProxy for load balancing
Set up HAProxy to distribute traffic across all MinIO nodes with health checking.
sudo apt install -y haproxy
Configure HAProxy for MinIO clustering
Create HAProxy configuration with SSL termination and automatic failover.
global
daemon
maxconn 4096
log stdout local0
stats socket /var/lib/haproxy/stats
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
option httplog
option dontlognull
option redispatch
retries 3
maxconn 2000
frontend minio_api
bind *:9000
bind *:9000 ssl crt /etc/minio/certs/public.crt
redirect scheme https if !{ ssl_fc }
default_backend minio_servers
frontend minio_console
bind *:9001
bind *:9001 ssl crt /etc/minio/certs/public.crt
redirect scheme https if !{ ssl_fc }
default_backend minio_console_servers
backend minio_servers
balance roundrobin
option httpchk GET /minio/health/live
server minio-1 203.0.113.10:9000 check inter 30s rise 2 fall 3
server minio-2 203.0.113.11:9000 check inter 30s rise 2 fall 3
server minio-3 203.0.113.12:9000 check inter 30s rise 2 fall 3
server minio-4 203.0.113.13:9000 check inter 30s rise 2 fall 3
server minio-5 203.0.113.14:9000 check inter 30s rise 2 fall 3
server minio-6 203.0.113.15:9000 check inter 30s rise 2 fall 3
backend minio_console_servers
balance roundrobin
server minio-1 203.0.113.10:9001 check inter 30s rise 2 fall 3
server minio-2 203.0.113.11:9001 check inter 30s rise 2 fall 3
server minio-3 203.0.113.12:9001 check inter 30s rise 2 fall 3
server minio-4 203.0.113.13:9001 check inter 30s rise 2 fall 3
server minio-5 203.0.113.14:9001 check inter 30s rise 2 fall 3
server minio-6 203.0.113.15:9001 check inter 30s rise 2 fall 3
listen stats
bind *:8080
stats enable
stats uri /stats
stats refresh 30s
stats admin if TRUE
Enable services and start cluster
Configure firewall rules
Open required ports for MinIO clustering and client access.
sudo ufw allow 9000/tcp comment "MinIO API"
sudo ufw allow 9001/tcp comment "MinIO Console"
sudo ufw allow 8080/tcp comment "HAProxy Stats"
sudo ufw reload
Start MinIO services on all nodes
Enable and start the MinIO service on each cluster node in sequence.
sudo systemctl daemon-reload
sudo systemctl enable minio
sudo systemctl start minio
sudo systemctl status minio
Start HAProxy load balancer
Enable HAProxy for automatic failover and load distribution.
sudo systemctl enable haproxy
sudo systemctl start haproxy
sudo systemctl status haproxy
Configure health monitoring and alerts
Create health monitoring script
Implement automated health checking with email alerts for node failures.
#!/bin/bash
MinIO Cluster Health Check
CLUSTER_NODES=("203.0.113.10" "203.0.113.11" "203.0.113.12" "203.0.113.13" "203.0.113.14" "203.0.113.15")
ALERT_EMAIL="admin@example.com"
LOG_FILE="/var/log/minio-health.log"
check_node_health() {
local node=$1
local response=$(curl -s -k --max-time 10 "https://${node}:9000/minio/health/live")
if [[ $? -eq 0 && "$response" == "OK" ]]; then
echo "$(date): Node $node is healthy" >> $LOG_FILE
return 0
else
echo "$(date): Node $node is unhealthy or unreachable" >> $LOG_FILE
echo "MinIO node $node is down or unhealthy. Please investigate immediately." | \
mail -s "MinIO Cluster Alert: Node $node Down" $ALERT_EMAIL
return 1
fi
}
Check all nodes
for node in "${CLUSTER_NODES[@]}"; do
check_node_health $node
done
Check cluster overall health
cluster_info=$(curl -s -k "https://203.0.113.10:9000/minio/health/cluster")
echo "$(date): Cluster status: $cluster_info" >> $LOG_FILE
Make health check script executable
Set proper permissions and ownership for the monitoring script.
sudo chmod 755 /usr/local/bin/minio-health-check.sh
sudo chown root:root /usr/local/bin/minio-health-check.sh
sudo touch /var/log/minio-health.log
sudo chown minio-user:minio-user /var/log/minio-health.log
Schedule automated health checks
Configure cron to run health checks every 5 minutes.
sudo crontab -e
# MinIO cluster health check every 5 minutes
/5 * /usr/local/bin/minio-health-check.sh
Verify your setup
Check cluster status
Verify that all nodes are running and participating in the cluster.
sudo systemctl status minio
curl -k https://minio-cluster.example.com:9000/minio/health/live
curl -k https://minio-cluster.example.com:9000/minio/health/ready
Test MinIO client connectivity
Install MinIO client and test cluster connectivity.
wget https://dl.min.io/client/mc/release/linux-amd64/mc
sudo chmod +x mc
sudo mv mc /usr/local/bin/
Configure client
mc alias set mycluster https://minio-cluster.example.com:9000 YOUR_ACCESS_KEY YOUR_SECRET_KEY
mc admin info mycluster
Test erasure coding and failover
Create a test bucket and verify data redundancy.
mc mb mycluster/test-bucket
echo "Test file for erasure coding" > test-file.txt
mc cp test-file.txt mycluster/test-bucket/
mc ls mycluster/test-bucket/
Monitor HAProxy statistics
Check load balancer status and backend server health.
curl http://203.0.113.10:8080/stats
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Nodes can't join cluster | Network connectivity issues | Check firewall rules and hostname resolution in /etc/hosts |
| Permission denied errors | Incorrect file ownership | sudo chown -R minio-user:minio-user /data/disk* |
| SSL certificate errors | Self-signed certificates | Use proper CA-signed certificates or configure client to accept self-signed |
| Split-brain scenarios | Network partitions | Ensure odd number of nodes and proper network redundancy |
| High memory usage | Large number of objects | Tune MINIO_HEAL_WORKERS and increase system memory |
| Slow performance | Disk I/O bottleneck | Use dedicated disks per node and optimize filesystem (XFS recommended) |
Next steps
- Implement MinIO security hardening with IAM policies and audit logging
- Configure MinIO backup and disaster recovery with automated snapshots and replication
- Set up MinIO gateway for multi-cloud storage federation with AWS S3 and Azure integration
- Monitor Kubernetes clusters with Prometheus and Grafana for container orchestration insights
- Configure MinIO monitoring with Prometheus and Grafana dashboards
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'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Global variables
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLEANUP_FILES=()
# Usage function
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -n, --nodes Comma-separated list of node IPs (default: 203.0.113.10,203.0.113.11,203.0.113.12,203.0.113.13,203.0.113.14,203.0.113.15)"
echo " -d, --domain Cluster domain name (default: minio-cluster.example.com)"
echo " -h, --help Show this help message"
echo ""
echo "Example:"
echo " $0 -n 10.0.1.10,10.0.1.11,10.0.1.12,10.0.1.13,10.0.1.14,10.0.1.15 -d minio.company.com"
exit 1
}
# Error handling with cleanup
cleanup() {
if [ ${#CLEANUP_FILES[@]} -gt 0 ]; then
echo -e "${YELLOW}[CLEANUP] Removing temporary files...${NC}"
for file in "${CLEANUP_FILES[@]}"; do
[ -f "$file" ] && rm -f "$file"
done
fi
}
trap cleanup ERR EXIT
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if running as root or with sudo
check_privileges() {
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root or with sudo"
exit 1
fi
}
# Detect distribution and set package manager
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
export PKG_MGR PKG_INSTALL PKG_UPDATE
log_info "Detected distribution: $ID"
else
log_error "Cannot detect distribution. /etc/os-release not found."
exit 1
fi
}
# Parse command line arguments
parse_args() {
NODES="203.0.113.10,203.0.113.11,203.0.113.12,203.0.113.13,203.0.113.14,203.0.113.15"
DOMAIN="minio-cluster.example.com"
while [[ $# -gt 0 ]]; do
case $1 in
-n|--nodes)
NODES="$2"
shift 2
;;
-d|--domain)
DOMAIN="$2"
shift 2
;;
-h|--help)
usage
;;
*)
log_error "Unknown option: $1"
usage
;;
esac
done
# Validate nodes
IFS=',' read -ra NODE_ARRAY <<< "$NODES"
if [ ${#NODE_ARRAY[@]} -lt 4 ]; then
log_error "MinIO clustering requires at least 4 nodes"
exit 1
fi
export NODES DOMAIN NODE_ARRAY
}
# Update system packages
update_system() {
echo -e "${BLUE}[1/10] Updating system packages...${NC}"
eval "$PKG_UPDATE"
eval "$PKG_INSTALL wget curl systemd openssl"
log_success "System packages updated"
}
# Create storage directories
create_storage() {
echo -e "${BLUE}[2/10] Creating storage directories...${NC}"
mkdir -p /data/disk{1,2,3,4}
chmod 755 /data/disk{1,2,3,4}
log_success "Storage directories created"
}
# Download and install MinIO binary
install_minio() {
echo -e "${BLUE}[3/10] Downloading and installing MinIO binary...${NC}"
cd /tmp
wget -q https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
mv minio /usr/local/bin/
ln -sf /usr/local/bin/minio /usr/local/bin/minio
log_success "MinIO binary installed"
}
# Create MinIO user and group
create_user() {
echo -e "${BLUE}[4/10] Creating MinIO user and group...${NC}"
if ! getent group minio-user > /dev/null 2>&1; then
groupadd -r minio-user
fi
if ! getent passwd minio-user > /dev/null 2>&1; then
useradd -M -r -g minio-user minio-user
fi
chown minio-user:minio-user /data/disk{1,2,3,4}
log_success "MinIO user created"
}
# Configure host resolution
configure_hosts() {
echo -e "${BLUE}[5/10] Configuring host resolution...${NC}"
# Remove existing MinIO entries
sed -i '/# MinIO Cluster Nodes/,/^$/d' /etc/hosts
# Add new entries
echo "# MinIO Cluster Nodes" >> /etc/hosts
local i=1
for node in "${NODE_ARRAY[@]}"; do
echo "$node minio-$i" >> /etc/hosts
((i++))
done
echo "" >> /etc/hosts
log_success "Host resolution configured"
}
# Create MinIO configuration
create_config() {
echo -e "${BLUE}[6/10] Creating MinIO configuration...${NC}"
mkdir -p /etc/minio
chmod 750 /etc/minio
# Generate credentials
ACCESS_KEY=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-20)
SECRET_KEY=$(openssl rand -base64 48 | tr -d "=+/" | cut -c1-40)
# Create volume string
local volume_string="http://minio-{1...${#NODE_ARRAY[@]}}/data/disk{1...4}"
# Create configuration file
cat > /etc/minio/minio.conf << EOF
# MinIO Cluster Configuration
MINIO_ROOT_USER="$ACCESS_KEY"
MINIO_ROOT_PASSWORD="$SECRET_KEY"
MINIO_VOLUMES="$volume_string"
MINIO_OPTS="--console-address :9001 --address :9000"
MINIO_SERVER_URL="https://$DOMAIN:9000"
MINIO_BROWSER_REDIRECT_URL="https://$DOMAIN:9001"
# Security and Performance
MINIO_PROMETHEUS_AUTH_TYPE="public"
MINIO_HEAL_WORKERS="4"
MINIO_HEAL_DRIVE_WORKERS="2"
EOF
chown minio-user:minio-user /etc/minio/minio.conf
chmod 640 /etc/minio/minio.conf
chown minio-user:minio-user /etc/minio
log_success "MinIO configuration created"
log_warning "Access Key: $ACCESS_KEY"
log_warning "Secret Key: $SECRET_KEY"
log_warning "Please save these credentials securely!"
}
# Create systemd service
create_service() {
echo -e "${BLUE}[7/10] Creating systemd service...${NC}"
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
AssertFileIsExecutable=/usr/local/bin/minio
[Service]
WorkingDirectory=/usr/local/
User=minio-user
Group=minio-user
ProtectProc=invisible
EnvironmentFile=/etc/minio/minio.conf
ExecStartPre=/bin/bash -c "if [ -z \"\${MINIO_VOLUMES}\" ]; then echo \"Variable MINIO_VOLUMES not set in /etc/minio/minio.conf\"; exit 1; fi"
ExecStart=/usr/local/bin/minio server \$MINIO_VOLUMES \$MINIO_OPTS
Restart=always
LimitNOFILE=65536
TasksMax=infinity
TimeoutStopSec=infinity
SendSIGKILL=no
[Install]
WantedBy=multi-user.target
EOF
chmod 644 /etc/systemd/system/minio.service
systemctl daemon-reload
log_success "Systemd service created"
}
# Configure firewall
configure_firewall() {
echo -e "${BLUE}[8/10] Configuring firewall...${NC}"
case "$PKG_MGR" in
apt)
if command -v ufw > /dev/null; then
ufw allow 9000/tcp comment "MinIO API"
ufw allow 9001/tcp comment "MinIO Console"
fi
;;
dnf|yum)
if command -v firewall-cmd > /dev/null; then
firewall-cmd --permanent --add-port=9000/tcp
firewall-cmd --permanent --add-port=9001/tcp
firewall-cmd --reload
fi
;;
esac
log_success "Firewall configured"
}
# Enable and start service
start_service() {
echo -e "${BLUE}[9/10] Enabling and starting MinIO service...${NC}"
systemctl enable minio
systemctl start minio
# Wait for service to start
sleep 5
if systemctl is-active --quiet minio; then
log_success "MinIO service started successfully"
else
log_error "MinIO service failed to start"
systemctl status minio --no-pager
exit 1
fi
}
# Verify installation
verify_installation() {
echo -e "${BLUE}[10/10] Verifying installation...${NC}"
# Check if service is running
if systemctl is-active --quiet minio; then
log_success "MinIO service is running"
else
log_error "MinIO service is not running"
return 1
fi
# Check if ports are listening
if netstat -tuln 2>/dev/null | grep -q ":9000\|:9001" || ss -tuln 2>/dev/null | grep -q ":9000\|:9001"; then
log_success "MinIO is listening on ports 9000 and 9001"
else
log_warning "MinIO ports may not be accessible yet"
fi
log_success "MinIO cluster installation completed!"
echo ""
echo -e "${GREEN}Next steps:${NC}"
echo "1. Run this script on all cluster nodes with the same configuration"
echo "2. Access MinIO console at: http://$(hostname -I | awk '{print $1}'):9001"
echo "3. Configure load balancer for production use"
echo "4. Set up SSL/TLS certificates for HTTPS access"
}
# Main execution
main() {
echo -e "${GREEN}MinIO High Availability Cluster Installation${NC}"
echo "=============================================="
check_privileges
detect_distro
parse_args "$@"
update_system
create_storage
install_minio
create_user
configure_hosts
create_config
create_service
configure_firewall
start_service
verify_installation
log_success "Installation completed successfully!"
}
# Run main function with all arguments
main "$@"
Review the script before running. Execute with: bash install.sh