Integrate MinIO with Kubernetes for persistent storage and object data management

Intermediate 45 min Apr 19, 2026 138 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Deploy MinIO on Kubernetes using the operator for scalable object storage. Configure persistent volumes, high availability tenants, and secure ingress with SSL certificates.

Prerequisites

  • Kubernetes cluster with admin access
  • kubectl configured
  • Ingress controller installed
  • Storage class configured
  • cert-manager for SSL certificates

What this solves

MinIO provides S3-compatible object storage that integrates seamlessly with Kubernetes workloads. This tutorial shows you how to deploy MinIO using the Kubernetes operator for production-grade object storage with persistent volumes, high availability configuration, and secure access through ingress controllers.

Prerequisites

  • Kubernetes cluster with kubectl configured
  • Cluster admin permissions
  • At least 3 worker nodes for high availability
  • Storage class configured (local-path, nfs, or cloud provider)
  • Ingress controller installed (nginx-ingress recommended)

Step-by-step installation

Install MinIO Kubernetes operator

Download and apply the MinIO operator which manages MinIO tenants and handles updates, scaling, and configuration management.

kubectl apply -k "github.com/minio/operator?ref=v6.0.4"

Verify operator installation

Check that the MinIO operator pods are running in the minio-operator namespace.

kubectl get pods -n minio-operator
kubectl get crd | grep minio

Create storage class for MinIO

Define a storage class with appropriate performance characteristics for MinIO workloads. This example uses local storage but adjust for your environment.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: minio-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
parameters:
  type: local
kubectl apply -f minio-storage-class.yaml

Create persistent volumes

Create persistent volumes for MinIO data storage. In production, use dedicated disks or network storage for better performance and reliability.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: minio-pv-1
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: minio-storage
  hostPath:
    path: /mnt/minio-storage-1
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: minio-pv-2
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: minio-storage
  hostPath:
    path: /mnt/minio-storage-2
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: minio-pv-3
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: minio-storage
  hostPath:
    path: /mnt/minio-storage-3
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: minio-pv-4
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: minio-storage
  hostPath:
    path: /mnt/minio-storage-4
kubectl apply -f minio-pv.yaml

Create MinIO tenant namespace

Create a dedicated namespace for the MinIO tenant to isolate resources and apply specific policies.

kubectl create namespace minio-tenant

Deploy MinIO tenant with high availability

Create a MinIO tenant configuration with multiple servers for high availability and data redundancy.

apiVersion: minio.min.io/v2
kind: Tenant
metadata:
  name: minio-cluster
  namespace: minio-tenant
spec:
  image: minio/minio:RELEASE.2024-01-16T16-07-38Z
  credsSecret:
    name: minio-creds-secret
  pools:
  - servers: 4
    name: pool-0
    volumesPerServer: 1
    volumeClaimTemplate:
      metadata:
        name: data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 100Gi
        storageClassName: minio-storage
    securityContext:
      runAsUser: 1000
      runAsGroup: 1000
      runAsNonRoot: true
    resources:
      requests:
        cpu: 250m
        memory: 512Mi
      limits:
        cpu: 500m
        memory: 1Gi
  mountPath: /export
  subPath: /data
  requestAutoCert: false
  certConfig:
    commonName: ""
    organizationName: []
    dnsNames: []
  externalCaCertSecret: []
  externalCertSecret: []
  externalClientCertSecrets: []
  configuration:
    name: minio-config-secret

Create MinIO credentials secret

Generate secure credentials for MinIO admin access. Use strong passwords in production environments.

kubectl create secret generic minio-creds-secret \
  --from-literal=accesskey="minio-admin" \
  --from-literal=secretkey="minio-secret-key-change-me" \
  -n minio-tenant

Create MinIO configuration secret

Configure MinIO server settings including region, browser access, and performance tuning options.

kubectl create secret generic minio-config-secret \
  --from-literal=config.env="export MINIO_REGION_NAME=us-east-1
export MINIO_BROWSER=on
export MINIO_SERVER_URL=https://minio.example.com
export MINIO_BROWSER_REDIRECT_URL=https://console.example.com" \
  -n minio-tenant

Deploy the MinIO tenant

Apply the tenant configuration to create the MinIO cluster with high availability setup.

kubectl apply -f minio-tenant.yaml

Wait for tenant deployment

Monitor the deployment progress and ensure all pods reach running state before proceeding.

kubectl get pods -n minio-tenant -w

Create TLS certificates for secure access

Generate TLS certificates for MinIO API and console access. This example uses cert-manager for automatic certificate management.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: minio-api-tls
  namespace: minio-tenant
spec:
  secretName: minio-api-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  commonName: minio.example.com
  dnsNames:
  - minio.example.com
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: minio-console-tls
  namespace: minio-tenant
spec:
  secretName: minio-console-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  commonName: console.example.com
  dnsNames:
  - console.example.com
kubectl apply -f minio-certificates.yaml

Configure ingress for MinIO API

Create ingress resource to expose MinIO API endpoint with SSL termination and proper headers for S3 compatibility.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minio-api-ingress
  namespace: minio-tenant
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "1024m"
    nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
  - hosts:
    - minio.example.com
    secretName: minio-api-tls
  rules:
  - host: minio.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: minio-cluster-hl
            port:
              number: 9000
kubectl apply -f minio-api-ingress.yaml

Configure ingress for MinIO console

Create ingress for the MinIO web console with proper WebSocket support for real-time monitoring.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minio-console-ingress
  namespace: minio-tenant
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "1024m"
    nginx.ingress.kubernetes.io/websocket-services: minio-cluster-console
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
  - hosts:
    - console.example.com
    secretName: minio-console-tls
  rules:
  - host: console.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: minio-cluster-console
            port:
              number: 9090
kubectl apply -f minio-console-ingress.yaml

Configure network policies for security

Implement network policies to restrict traffic to MinIO services and enhance security posture.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: minio-network-policy
  namespace: minio-tenant
spec:
  podSelector:
    matchLabels:
      app: minio
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 9000
    - protocol: TCP
      port: 9090
  - from:
    - podSelector:
        matchLabels:
          app: minio
    ports:
    - protocol: TCP
      port: 9000
  egress:
  - to: []
    ports:
    - protocol: TCP
      port: 53
    - protocol: UDP
      port: 53
  - to:
    - podSelector:
        matchLabels:
          app: minio
    ports:
    - protocol: TCP
      port: 9000
kubectl apply -f minio-network-policy.yaml

Set up monitoring with Prometheus integration

Configure MinIO to expose metrics for Prometheus monitoring and create service monitor for automatic discovery.

apiVersion: v1
kind: Service
metadata:
  name: minio-cluster-metrics
  namespace: minio-tenant
  labels:
    app: minio
    monitoring: prometheus
spec:
  ports:
  - name: http-metrics
    port: 9000
    protocol: TCP
    targetPort: 9000
  selector:
    app: minio
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: minio-cluster-monitor
  namespace: minio-tenant
  labels:
    app: minio
spec:
  selector:
    matchLabels:
      app: minio
      monitoring: prometheus
  endpoints:
  - port: http-metrics
    interval: 30s
    path: /minio/v2/metrics/cluster
    scheme: http
kubectl apply -f minio-monitoring.yaml

Configure backup automation

Set up automated backups using MinIO mc client in a CronJob for data protection and disaster recovery.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: minio-backup
  namespace: minio-tenant
spec:
  schedule: "0 2   *"  # Daily at 2 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: minio-backup
            image: minio/mc:latest
            command:
            - /bin/sh
            - -c
            - |
              mc alias set source https://minio.example.com $MINIO_ACCESS_KEY $MINIO_SECRET_KEY
              mc alias set backup s3://backup-bucket $BACKUP_ACCESS_KEY $BACKUP_SECRET_KEY
              mc mirror source/ backup/$(date +%Y-%m-%d)/
            env:
            - name: MINIO_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: minio-creds-secret
                  key: accesskey
            - name: MINIO_SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: minio-creds-secret
                  key: secretkey
            - name: BACKUP_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: backup-credentials
                  key: access-key
            - name: BACKUP_SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: backup-credentials
                  key: secret-key
          restartPolicy: OnFailure
kubectl apply -f minio-backup-cronjob.yaml

Verify your setup

Check that all components are running and accessible:

# Check MinIO tenant status
kubectl get tenant -n minio-tenant

Verify all pods are running

kubectl get pods -n minio-tenant

Check persistent volume claims

kubectl get pvc -n minio-tenant

Test ingress connectivity

curl -I https://minio.example.com/minio/health/live

Check MinIO cluster status via mc client

kubectl run -it --rm minio-client --image=minio/mc:latest --restart=Never -- \ mc alias set k8s-minio https://minio.example.com minio-admin minio-secret-key-change-me kubectl run -it --rm minio-client --image=minio/mc:latest --restart=Never -- \ mc admin info k8s-minio

Access the MinIO console at https://console.example.com using the credentials from your secret.

Configure client applications

Applications can access MinIO using standard S3 SDKs. Here's a configuration example for connecting applications:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-minio-config
data:
  endpoint: "https://minio.example.com"
  region: "us-east-1"
  bucket: "app-data"
---
apiVersion: v1
kind: Secret
metadata:
  name: app-minio-credentials
type: Opaque
data:
  access-key: bWluaW8tYWRtaW4=  # base64 encoded
  secret-key: bWluaW8tc2VjcmV0LWtleS1jaGFuZ2UtbWU=  # base64 encoded
Note: For production applications, create separate service accounts and IAM policies for each application instead of using admin credentials. You can also integrate with existing monitoring systems by linking to our MinIO Prometheus monitoring tutorial for comprehensive observability.

Common issues

SymptomCauseFix
Pods stuck in Pending stateNo available persistent volumesCreate more PVs or check storage class configuration
MinIO API returns 503 errorsNot enough healthy servers for quorumEnsure at least N/2+1 servers are running where N is total servers
Console shows "Invalid credentials"Wrong secret values or encodingVerify secret contains correct base64 encoded values
Ingress returns 502 Bad GatewayService name mismatch or wrong portsCheck service names match ingress backend configuration
TLS certificate not workingcert-manager not properly configuredVerify ClusterIssuer exists and check certificate status
Poor performance during I/O operationsStorage not optimized for MinIOUse dedicated SSDs and adjust storage class parameters
Backup job failingNetwork policies blocking external accessUpdate network policies to allow egress for backup destinations

Production optimization

For production deployments, consider these additional configurations:

Resource quotas and limits

Set appropriate resource quotas to prevent resource exhaustion and ensure predictable performance.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: minio-quota
  namespace: minio-tenant
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 4Gi
    limits.cpu: "4"
    limits.memory: 8Gi
    persistentvolumeclaims: "8"

Pod disruption budgets

Configure pod disruption budgets to ensure high availability during cluster maintenance.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: minio-pdb
  namespace: minio-tenant
spec:
  minAvailable: 75%
  selector:
    matchLabels:
      app: minio

Next steps

Running this in production?

Want this handled for you? Setting up MinIO with Kubernetes once is straightforward. Keeping it patched, monitored, backed up and tuned across environments is the harder part. See how we run infrastructure like this for European SaaS and e-commerce teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.