Cómo Configurar MinIO como Almacenamiento S3 Local en Linux: Guía Completa para Sysadmins
Hace un año monté mi primer MinIO en un homelab para alejar los backups de PostgreSQL del mismo disco donde corría la base de datos. Pensé que la parte difícil iba a ser configurar el cluster distribuido con erasure coding; la parte difícil terminó siendo decidir entre MinIO, SeaweedFS y Ceph para un homelab donde lo único distribuido es la paranoia. Esta guía documenta esa decisión, con la instalación limpia de MinIO sobre Debian 12, los tres errores de permisos que me comí la primera tarde y el procedimiento de upgrade entre minor versions sin perder datos.
Si tu caso es un homelab o un servidor único que necesita storage S3-compatible para backups, fotos, artifacts de CI o un Nextcloud pesado, MinIO pelado es probablemente la opción correcta. Si tienes tres nodos, Ceph o SeaweedFS ya entran en la conversación — pero no antes.
TL;DR — Lo que vas a tener al final
- Tiempo de setup: 30-50 minutos para single-node, 2-3 horas para cluster de 4 nodos.
- Requisitos: 1-4 nodos Linux con disco dedicado para data, root, mínimo 4 GB RAM por nodo.
- Resultado: API S3 completa, throughput ~500 MB/s en single-node con NVMe, ~2 GB/s en cluster 4-node.
- Troubleshooting típico: permisos de /data, puertos 9000/9001 en firewall, mc admin info da la diagnosis en 5 segundos.
Por qué MinIO sigue siendo la opción S3-compatible en 2026
Si administras un homelab o un servidor Linux y tu aplicación ya habla S3 (o la puedes hacer hablar S3 con un cambio de variable de entorno), MinIO te da lo que ni NFS ni SMB pueden: object storage HTTP con API idéntica a la de Amazon S3, replicación entre nodos, y erasure coding nativo sin pelearte con un cluster Ceph. La licencia AGPLv3 desde abril de 2021 no cambia nada para self-hosting puro — solo te obliga a liberar el código si montas un servicio público que compita directamente con MinIO Inc.
Esta guía asume Debian 13 / Ubuntu 24.04 LTS como host, una instalación limpia de MinIO por Docker Compose con volúmenes nombrados, y un setup pensado para crecer de un disco a cuatro sin reescribir nada. Si quieres la instalación con binario directo en systemd y Caddy como reverse proxy, hay una guía complementaria en este sitio; este artículo se enfoca en el camino Docker + multi-disco + monitoring.
Requisitos del host: filesystem, discos y memoria
Antes de tocar Docker conviene verificar tres cosas que determinan si tu MinIO va a funcionar bien o se va a arrastrar:
- Filesystem con extended attributes: MinIO usa
xattrpara metadata por archivo y para su formato binario propio.ext4yxfslos tienen activos por default;btrfstambién;zfslos tiene pero con límites por dataset que debes respetar. No uses FAT, exFAT ni NTFS — el chequeo de xattr al inicio falla y MinIO se niega a arrancar. - Discos separados, no particiones del sistema: MinIO crea un subdirectorio por disco bajo el volumen y hace round-robin entre ellos. Si apuntas
MINIO_VOLUMES=/mnt/disco1/data /mnt/disco2/data /mnt/disco3/data /mnt/disco4/data(cuatro paths), MinIO distribuye los objetos y aplica erasure coding con tolerancia a uno perdido (paridad 1/4). Si le pasas un solo directorio, no hay redundancia — un disco muerto y pierdes todo. - Memoria RAM suficiente para el set de datos activo: MinIO mantiene un cache de objetos en RAM. La regla empírica es 1 GB de RAM por cada 1 TB de datos almacenados para un homelab con acceso moderado. Para 8 TB de backups, 8 GB de RAM es el mínimo cómodo; 16 GB es lo que recomendamos.
Verifica el filesystem antes de continuar:
df -T /mnt/disco1
stat -f -c '%T' /mnt/disco1
xattr -l /mnt/disco1/.testfile 2>&1 || echo "xattr no soportado"
Si stat -f -c '%T' devuelve algo distinto a ext4/xfs/btrfs/zfs, no sigas hasta cambiar el filesystem. La línea xattr -l debe listar atributos (aunque el archivo esté vacío) — si dice Operation not supported, monta con la opción user_xattr en /etc/fstab o elige otro FS.
Crear el dataset ZFS o Btrfs ideal
Si vas a dedicar discos grandes (>2 TB cada uno), un dataset ZFS o Btrfs con compresión te ahorra entre 10% y 30% del espacio para backups, y los snapshots instantáneos te permiten revertir cambios destructivos en segundos. ZFS es nuestra recomendación principal por madurez y predictibilidad; Btrfs es válido si ya lo tienes en el host.
sudo zpool create -o ashift=12 miniopool /dev/disk/by-id/disco1 /dev/disk/by-id/disco2
sudo zfs create -o compression=zstd -o atime=off -o recordsize=1M minio-data/data
sudo zfs create -o compression=zstd -o atime=off -o recordsize=1M minio-data/config
sudo zfs set quota=4T minio-data/data
El ashift=12 fuerza sectores de 4 KB (lo correcto en discos modernos). El recordsize=1M optimiza para objetos grandes típicos de MinIO (backups, videos, modelos ML); para archivos muy pequeños (< 16 KB) considera recordsize=16K. La compresión zstd es prácticamente gratis en CPU moderna. La quota=4T previene que un bucket descontrolado llene el pool entero.
Si prefieres Btrfs:
sudo mkfs.btrfs -L minio-data /dev/disk/by-id/disco1 /dev/disk/by-id/disco2
sudo mkdir -p /mnt/minio-data
sudo mount -o compress=zstd,autodefrag,noatime,discard=async /dev/disk/by-id/disco1 /mnt/minio-data
sudo btrfs quota enable /mnt/minio-data
Btrfs necesita un solo device para arrancar pero escala agregando más discos como perfiles de RAID. Para multi-disco con redundancia:
sudo btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt/minio-data
Este paso convierte los metadatos y los datos a RAID1 (espejo), tolerando la pérdida de un disco. Toma minutos u horas según el tamaño del pool — no lo interrumpas.
Docker Compose con multi-disco y healthcheck serio
El compose de abajo asume que MINIO_VOLUMES apunta a los cuatro paths montados de ZFS/Btrfs. MinIO detecta cada path como un disco lógico y aplica erasure coding EC:2 sobre el conjunto. Si un día agregas un quinto disco, MinIO rebalancea automáticamente la próxima vez que arranca con el nuevo path.
sudo mkdir -p /opt/minio && cd /opt/minio
sudo tee .env <<'ENVEOF'
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=$(openssl rand -base64 32)
MINIO_VOLUMES=/minio-data/disk1 /minio-data/disk2 /minio-data/disk3 /minio-data/disk4
MINIO_OPTS=--console-address :9001
ENVEOF
sudo chmod 600 .env
sudo chown -R 1000:1000 /opt/minio /minio-data
La contraseña se genera con openssl rand -base64 32 y se guarda en .env con permisos 600 (solo root la lee). Nunca la pongas en el docker-compose.yml ni en variables hardcoded — el archivo .env es la única fuente.
Ahora el compose:
sudo tee docker-compose.yml <<'COMPOSEEOF'
services:
minio:
image: minio/minio:RELEASE.2026-01-15T17-40-00Z
container_name: minio
restart: unless-stopped
user: "1000:1000"
env_file:
- .env
command: server ${MINIO_VOLUMES} --console-address :9001
volumes:
- /minio-data:/minio-data
ports:
- "127.0.0.1:9000:9000"
- "127.0.0.1:9001:9001"
healthcheck:
test: ["CMD", "/usr/bin/mc", "ready", "local"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
logging:
driver: json-file
options:
max-size: "50m"
max-file: "5"
ulimits:
nofile:
soft: 65536
hard: 65536
COMPOSEEOF
sudo docker compose up -d
El ulimits.nofile: 65536 es importante: MinIO abre miles de file descriptors en paralelo, especialmente cuando subes muchos objetos pequeños. El default de Linux suele ser 1024, lo cual satura MinIO en cuanto recibe carga real.
Verifica que arrancó y reconoció los cuatro discos:
sudo docker compose logs minio | grep -E 'API:|Status:|Drives:'
sudo docker compose exec minio mc admin info local
Debes ver una línea Drives: 4/4 OK en el output. Si dice Drives: 1/4 OK, MinIO solo ve un disco — revisa los permisos de /minio-data/disk{1..4} y que el UID 1000 dentro del contenedor coincida con el dueño en el host.
Cliente mc: el reemplazo de la consola web
Desde mediados de 2025 la consola web de MinIO dejó de existir en la edición community. Toda la administración — crear buckets, asignar políticas, rotar claves, ver métricas — se hace con el cliente mc o con llamadas directas a la API S3. Instálalo en tu máquina de administración (tu desktop, no el server):
curl -O https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/
mc --version
Crea el alias apuntando al server por HTTPS (asumiendo que ya hay un reverse proxy — si no, usa http://IP:9000 temporalmente):
mc alias set local https://minio.tudominio.example minioadmin "$(sudo cat /opt/minio/.env | grep MINIO_ROOT_PASSWORD | cut -d= -f2)" --api S3v4
mc admin info local
El --api S3v4 fuerza la firma v4 que espera cualquier cliente S3 moderno. mc admin info lista versión, uso por disco y uptime — si dice Servers: 1/1 y Drives: 4/4 OK, todo está en pie.
Un comando que merece la pena aprender desde el primer día: mc watch. Te muestra eventos en vivo del bucket (subidas, borrados, fallos de IAM) sin necesidad de Prometheus:
mc watch -h 5s local/backups
Para producción real, sustitúyelo por el endpoint de Prometheus que MinIO expone en :9000/minio/v2/metrics/admin y :9000/minio/v2/metrics/cluster, pero mc watch es el camino más rápido para validar que algo se está subiendo.
Hardening del host: firewall, fail2ban y secretos
El puerto 9000 del API S3 debe quedar expuesto solo a la red local o al reverse proxy. El 9001 de la consola ya no tiene UI pero sigue respondiendo HTTP — bloquearlo reduce superficie de ataque.
sudo ufw default deny incoming
sudo ufw allow from 192.168.0.0/16 to any port 9000 proto tcp comment "minio-s3-api"
sudo ufw deny 9001
sudo ufw reload
ss -tlnp | grep -E '9000|9001'
El ss final debe mostrar el 9000 escuchado por docker-proxy y el 9001 también pero solo en loopback. Si ss los muestra en 0.0.0.0:9000, el contenedor está mal configurado — revisa ports: en el compose.
Para fail2ban, MinIO emite logs en formato JSON que puedes parsear con un filter personalizado. Un intento de login con clave incorrecta aparece como:
sudo grep -E 'Request from' /var/lib/docker/containers/*/minio-log.json | head -5
Si ves ráfagas desde la misma IP, fail2ban con un filter que matchee "remoteIP":"<ip>","requestURL":".*/login.*" las bloquea automáticamente. Configuración típica en /etc/fail2ban/filter.d/minio.conf:
[Definition]
failregex = "remoteIP":"<HOST>","requestURL":".*(login|admin).*"
ignoreregex =
Y en /etc/fail2ban/jail.local:
[minio]
enabled = true
filter = minio
logpath = /var/lib/docker/containers/*/minio-log.json
maxretry = 5
bantime = 3600
Reinicia fail2ban y verifica: sudo fail2ban-client status minio. Las IPs que acumulen 5 fallos en 10 minutos quedan bloqueadas una hora.
Disaster recovery: backup del state y restore de un bucket
MinIO almacena los objetos binarios en los discos y un set de metadata en cada disco (formato xl.meta). Si pierdes un disco del pool, MinIO reconstruye los datos desde los otros tres con la paridad EC:2. Pero si pierdes dos discos simultáneamente, o un escenario peor (filesystem corrupto, ransomware que cifró los datos), necesitas una copia externa.
La estrategia más simple: un cron que sincroniza buckets críticos a otro MinIO en otra máquina con mc mirror:
# /etc/cron.d/minio-mirror
0 3 * * * root mc mirror --overwrite --remove --watch /minio-remote/backups /local/backups >> /var/log/minio-mirror.log 2>&1
El --watch queda en foreground; para cron real conviene ejecutarlo en background con un systemd service. La copia remota puede ser otro MinIO, un bucket de Backblaze B2 (compatible S3 vía mc alias) o un Glacier de AWS para archivo frío.
Para recuperar un objeto borrado por accidente:
mc version list local/backups
mc version info local/backups/objeto-borrado.bin
mc cp local/backups/objeto-borrado.bin&versionId=XYZ /tmp/recuperado.bin
MinIO mantiene versionado por bucket si lo activas. Para activarlo:
mc version enable local/backups
mc retention set --default 30d local/backups
La retención de 30 días es ajustable por bucket. Si necesitas versionado más largo para compliance, sube a 365 días o indefinido con --default 3650d. Cuidado: el versionado multiplica el espacio usado por el factor de cambios del bucket, así que no lo actives en buckets donde reescribas los mismos objetos cada hora.
Monitoreo con el exporter de Prometheus
MinIO trae un exporter Prometheus nativo en el puerto 49153 si lo activas. Configúralo vía mc:
mc admin config set local prometheus auth_token=$(openssl rand -hex 32)
mc admin service restart local
El auth_token es el bearer token que Prometheus envía en cada scrape. Anótalo en tu /etc/prometheus/prometheus.yml:
scrape_configs:
- job_name: minio
scheme: http
static_configs:
- targets: ['127.0.0.1:9000']
metrics_path: /minio/v2/metrics/cluster
bearer_token: TU_TOKEN_DE_32_BYTES
- job_name: minio_node
scheme: http
static_configs:
- targets: ['127.0.0.1:9000']
metrics_path: /minio/v2/metrics/node
bearer_token: TU_TOKEN_DE_32_BYTES
Las métricas clave que importan en un dashboard de Grafana:
minio_disk_storage_available— espacio libre por disco. Alerta si baja del 15%.minio_disk_storage_used— uso actual por disco.minio_s3_requests_total— requests por segundo por tipo (GET/PUT/DELETE).minio_s3_requests_4xx_errors_total— errores de cliente (claves malas, bucket inexistente).minio_s3_requests_5xx_errors_total— errores de servidor (problemas reales).minio_node_disk_free_inodes— inodes libres. MinIO crea un archivo por objeto; un bucket con millones de objetos pequeños puede quedarse sin inodes antes que sin espacio.
Un dashboard mínimo en Grafana con seis paneles cubre el 90% de las necesidades operativas de un homelab. Para producción, añade paneles de latencia (minio_s3_request_time_seconds_bucket) y de red (node_network_* del exporter de node).
Troubleshooting: errores típicos de un MinIO mal configurado
“Read failed. Insufficient number of disks online” al hacer mc admin info. Significa que MinIO no puede contactar al menos la mitad de los discos. Verifica cada path del MINIO_VOLUMES:
for d in /minio-data/disk1 /minio-data/disk2 /minio-data/disk3 /minio-data/disk4; do
sudo ls -la $d | head -2
sudo touch $d/.write-test && sudo rm $d/.write-test && echo "$d OK" || echo "$d FAIL"
done
Si un path devuelve FAIL, suele ser que el filesystem está montado read-only (mount | grep $d) o que se llenó el quota de ZFS/Btrfs (zfs list/btrfs qgroup show).
“Drive size mismatch among nodes” al añadir un disco nuevo. MinIO espera que todos los discos tengan el mismo tamaño para EC uniforme. Si el disco nuevo es más grande, MinIO solo usa el equivalente al más pequeño y desperdicia el resto. La solución es particionar el disco nuevo al tamaño exacto:
sudo parted /dev/disco-nuevo mklabel gpt
sudo parted /dev/disco-nuevo mkpart primary 0% 100%
sudo mkfs.xfs -L minio-disk5 /dev/disco-nuevo-part1
sudo mount /dev/disco-nuevo-part1 /minio-data/disk5
Después añade el path a MINIO_VOLUMES en el .env, reinicia el contenedor con docker compose up -d y MinIO rebalancea los datos al nuevo disco automáticamente.
“SignatureDoesNotMatch” en mc pero el login funciona. El reloj del cliente difiere del servidor por más de 15 minutos. La firma v4 rechaza requests con timestamps fuera de la ventana. Solución:
sudo timedatectl set-ntp true
sudo systemctl restart systemd-timesyncd
Verifica con date en cliente y servidor: deben coincidir en el segundo.
“XMinioServerNotInitialized: Server not initialized”. El servidor arrancó pero no completó la inicialización del primer disco. Borra el contenido del directorio del primer disco (NO los otros) y reinicia:
sudo systemctl stop minio
sudo rm -rf /minio-data/disk1/.minio.sys
sudo systemctl start minio
Solo en el primer disco, y solo si está vacío. Si ya tienes datos, este error indica filesystem corrupto y necesitas restore desde el mirror.
Cuándo NO usar MinIO: tres casos honestos
MinIO no es la respuesta universal para object storage en Linux. Hay tres casos donde es mejor opción otra cosa:
- Archivos muy pequeños (< 1 KB) en millones. Cada objeto MinIO consume un inode y un par de KB de metadata en disco. Para una colección de 50 millones de thumbnails de 8 KB, MinIO usa 2-3x más espacio que SeaweedFS o Garage. Si tu caso es ese, mira SeaweedFS.
- Block storage sobre el mismo Linux server. MinIO es object storage con API HTTP. Si lo que necesitas es montar un volumen tipo EBS en una VM, MinIO no te sirve — usa LVM, ZFS o Ceph RBD directamente.
- Multi-tenant con cuotas estrictas por usuario. MinIO soporta quotas por bucket, pero el modelo de cuentas no es tan granular como Ceph RGW o AWS S3 real. Para un cluster con 50 usuarios y políticas complejas de billing, Ceph o una suscripción a un S3 comercial son menos dolorosos.
Resumen: checklist de despliegue
Antes de dar por terminado un MinIO en producción, recorre este checklist:
- Filesystem con
xattractivo (ext4/xfs/btrfs/zfs). Verificado constat -f -c '%T'. - Cuatro paths distintos en
MINIO_VOLUMES, cada uno montado desde un disco físico diferente. - Contraseña de root con al menos 32 bytes aleatorios (
openssl rand -base64 32), guardada en.envcon permisos 600. - Healthcheck funcionando:
mc admin info localdevuelveDrives: 4/4 OK. - Firewall: puerto 9000 accesible solo desde la red local o el reverse proxy. Puerto 9001 cerrado o restringido a loopback.
- Reverse proxy con TLS válido frente al puerto 9000 (Caddy, Nginx o Traefik). No exponer 9000 directo a internet.
- Usuario dedicado por aplicación con política IAM limitada al bucket correspondiente. Root
minioadminsolo para emergencias. - Versionado activado en buckets críticos (
mc version enable). - Mirror remoto con
mc mirrorcorriendo diario a otro MinIO o a un bucket comercial S3. - Prometheus scrapeando
:9000/minio/v2/metrics/clustercon dashboards en Grafana. - Logs de MinIO parseados por fail2ban con ban automático tras 5 intentos fallidos.
Preguntas frecuentes
¿Por qué Cómo Configurar MinIO como Almacenamiento S3 Local en Linux: Guía Completa para Sysadmins es relevante para sysadmins Linux?
Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.
¿Qué necesito para empezar con Cómo Configurar MinIO como Almacenamiento?
Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.
¿Cuáles son los errores más comunes con Cómo Configurar MinIO como Almacenamiento?
Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.
¿Cuándo usar Cómo Configurar MinIO como Almacenamiento y cuándo una alternativa?
Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.
¿Cómo migro desde una configuración previa a Cómo Configurar MinIO como Almacenamiento?
Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.
Lee también
- Guía Fedora 44: Novedades, Instalación y Upgrade para Sysadmins Linux
- Configura VLANs en Linux con nmcli: Guía paso a paso para sysadmins
- Protege tu SSH: Guía esencial para configurar Fail2ban en Linux
- Guía Definitiva: Configura Kubernetes con kubeadm en Ubuntu para Sysadmins Linux
- Configura Prometheus y Grafana: Guía DevOps para SysAdmins Linux
