Introduction
MinIO is a high-performance S3-compatible object storage system — ideal for backups, artifacts, data lakes, and any workload needing an S3 API. Ansible automates the full deployment: single-node or distributed cluster, TLS encryption, bucket policies, lifecycle rules, site replication, and Prometheus monitoring.
Single-Node Deployment
---
- name: Deploy MinIO single node
hosts: minio_servers
become: true
vars:
minio_root_user: minioadmin
minio_root_password: "{{ vault_minio_root_password }}"
minio_data_dir: /data/minio
minio_console_port: 9001
minio_api_port: 9000
tasks:
- name: Create MinIO user
ansible.builtin.user:
name: minio-user
system: true
shell: /usr/sbin/nologin
- name: Create data directory
ansible.builtin.file:
path: "{{ minio_data_dir }}"
state: directory
owner: minio-user
group: minio-user
mode: '0750'
- name: Download MinIO
ansible.builtin.get_url:
url: https://dl.min.io/server/minio/release/linux-amd64/minio
dest: /usr/local/bin/minio
mode: '0755'
- name: Deploy MinIO environment file
ansible.builtin.template:
src: minio.env.j2
dest: /etc/default/minio
mode: '0600'
notify: restart minio
- name: Create systemd service
ansible.builtin.copy:
dest: /etc/systemd/system/minio.service
content: |
[Unit]
Description=MinIO Object Storage
After=network-online.target
[Service]
User=minio-user
Group=minio-user
EnvironmentFile=/etc/default/minio
ExecStart=/usr/local/bin/minio server $MINIO_OPTS
Restart=always
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
mode: '0644'
notify:
- daemon reload
- restart minio
- name: Allow MinIO through firewall
ansible.posix.firewalld:
port: "{{ item }}/tcp"
permanent: true
state: enabled
immediate: true
loop:
- "{{ minio_api_port }}"
- "{{ minio_console_port }}"
- name: Start MinIO
ansible.builtin.service:
name: minio
state: started
enabled: true
handlers:
- name: daemon reload
ansible.builtin.systemd:
daemon_reload: true
- name: restart minio
ansible.builtin.service:
name: minio
state: restarted
Environment Template
# templates/minio.env.j2
MINIO_ROOT_USER={{ minio_root_user }}
MINIO_ROOT_PASSWORD={{ minio_root_password }}
MINIO_OPTS="{{ minio_data_dir }} --console-address :{{ minio_console_port }} --address :{{ minio_api_port }}"
{% if minio_tls_cert is defined %}
MINIO_CERTS_DIR=/etc/minio/certs
{% endif %}
{% if minio_prometheus_auth_type is defined %}
MINIO_PROMETHEUS_AUTH_TYPE={{ minio_prometheus_auth_type }}
{% endif %}
Distributed Cluster
# For 4-node distributed MinIO cluster
# group_vars/minio_cluster/main.yml
minio_nodes:
- minio1.example.com
- minio2.example.com
- minio3.example.com
- minio4.example.com
minio_data_dirs:
- /data/disk1
- /data/disk2
# templates/minio-cluster.env.j2
MINIO_ROOT_USER={{ minio_root_user }}
MINIO_ROOT_PASSWORD={{ minio_root_password }}
MINIO_OPTS="{% for node in minio_nodes %}{% for disk in minio_data_dirs %}http://{{ node }}:{{ minio_api_port }}{{ disk }} {% endfor %}{% endfor %} --console-address :{{ minio_console_port }}"
MINIO_SERVER_URL=https://minio.example.com:{{ minio_api_port }}
Install MinIO Client (mc)
- name: Install MinIO client
ansible.builtin.get_url:
url: https://dl.min.io/client/mc/release/linux-amd64/mc
dest: /usr/local/bin/mc
mode: '0755'
- name: Configure mc alias
ansible.builtin.command: >
mc alias set myminio http://localhost:{{ minio_api_port }}
{{ minio_root_user }} {{ minio_root_password }}
changed_when: true
no_log: true
Manage Buckets
- name: Create buckets
ansible.builtin.command: "mc mb myminio/{{ item }}"
loop:
- backups
- artifacts
- logs
- data-lake
register: bucket_result
changed_when: "'Bucket created' in bucket_result.stdout"
failed_when: false
- name: Set bucket versioning
ansible.builtin.command: "mc version enable myminio/{{ item }}"
loop: [backups, data-lake]
changed_when: true
- name: Set lifecycle policy (expire old versions after 30 days)
ansible.builtin.copy:
dest: /tmp/lifecycle-backups.json
content: |
{
"Rules": [{
"ID": "expire-old-versions",
"Status": "Enabled",
"NoncurrentVersionExpiration": {
"NoncurrentDays": 30
}
}]
}
mode: '0644'
- name: Apply lifecycle policy
ansible.builtin.command: mc ilm import myminio/backups < /tmp/lifecycle-backups.json
changed_when: true
TLS Configuration
- name: Create certs directory
ansible.builtin.file:
path: /etc/minio/certs
state: directory
owner: minio-user
mode: '0700'
- name: Copy TLS certificate
ansible.builtin.copy:
src: "{{ minio_tls_cert }}"
dest: /etc/minio/certs/public.crt
owner: minio-user
mode: '0644'
- name: Copy TLS private key
ansible.builtin.copy:
src: "{{ minio_tls_key }}"
dest: /etc/minio/certs/private.key
owner: minio-user
mode: '0600'
notify: restart minio
Nginx Reverse Proxy
- name: Deploy MinIO Nginx proxy
ansible.builtin.template:
src: minio-nginx.conf.j2
dest: /etc/nginx/conf.d/minio.conf
mode: '0644'
notify: reload nginx
# templates/minio-nginx.conf.j2
upstream minio_api {
{% for node in minio_nodes | default([inventory_hostname]) %}
server {{ node }}:{{ minio_api_port }};
{% endfor %}
}
server {
listen 443 ssl;
server_name minio.example.com;
ssl_certificate /etc/ssl/certs/minio.crt;
ssl_certificate_key /etc/ssl/private/minio.key;
client_max_body_size 0;
proxy_buffering off;
location / {
proxy_pass http://minio_api;
proxy_set_header Host $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;
}
}
Health Check
- name: Check MinIO health
ansible.builtin.uri:
url: "http://localhost:{{ minio_api_port }}/minio/health/live"
status_code: 200
- name: Check cluster health
ansible.builtin.uri:
url: "http://localhost:{{ minio_api_port }}/minio/health/cluster"
status_code: 200
when: minio_nodes is defined
Troubleshooting
Disk Space
- name: Check disk usage on MinIO data dirs
ansible.builtin.command: "df -h {{ minio_data_dir }}"
register: disk_usage
changed_when: false
Connection Issues
- name: Test MinIO API
ansible.builtin.uri:
url: "http://localhost:{{ minio_api_port }}/minio/health/live"
timeout: 5
register: health
ignore_errors: true
Related Articles
Conclusion
MinIO gives you an S3-compatible API anywhere — on-premises, edge, or cloud. Ansible deploys single-node instances for dev and distributed clusters for production, manages TLS, creates buckets with versioning and lifecycle policies, and monitors health. Object storage as code means reproducible, version-controlled storage infrastructure.