MinIO s3-compatible block storage on Windows Server 2019 and rclone snapshots

Using the Windows MinIO binary, I deploy s3-compatible storage on my Windows servers. I use erasure coding, striped across 4 (or more) directly attached drives. I can then configure rclone to sync/snapshot data to the object store. I’ll usually run additional MinIO instances on the same sets of drives for separate security domains/different FQDNs, pointing each instance to a different folder on the disks. The below is an example configuration, using the minio-00 folder on each disk.

Grab latest MinIO binary, install it as a Windows Service, and allow network access

The MinIO deployment script1 is modified slightly to do erasure coding across 4 drives, H:, I:, J: and K: in this example.

if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit }

Set-Location -Path $PSScriptRoot

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri "" -OutFile "minio-service.exe"
Invoke-WebRequest -Uri "" -OutFile "minio.exe"

$config = @'
  <description>MinIO is a high performance object storage server</description>
  <env name="MINIO_ACCESS_KEY" value="minio"/>
  <env name="MINIO_SECRET_KEY" value="123minio123"/>
  <arguments>server H:\minio-00 I:\minio-00 J:\minio-00 K:\minio-00</arguments>

Set-Content "minio-service.xml" $config

Start-Process -WorkingDirectory $PSScriptRoot -FilePath "$($PSScriptRoot)\minio-service.exe" -ArgumentList "install" -NoNewWindow -PassThru -Wait

Write-Host "Installation done"

New-NetFirewallRule -Program "$($PSScriptRoot)\minio.exe" -Action Allow -Profile Domain, Private, Public -DisplayName "MinIO" -Description "MinIO Server" -Direction Inbound

Powershell script to do health check on MinIO

Next, we deploy a PowerShell script that can be run locally, does an HTTP health check on MinIO, and restarts it if necessary. I’ve included some logging options here.


$logFile = "$(gc env:COMPUTERNAME).checkMinio-00.log"
$healthURL = "http://localhost:9000/minio/health/ready"
$desiredStatusCode = 200

function LogWrite {
  Param (

    $Stamp = (Get-Date).toString("yyyyMMdd HH:mm:ss ")
    $Line = "$Stamp $logString"

    if ($logFile) {
      Add-Content $logFile -Value $Line
    Else {
      Write-Output $Line

function Get-URLStatusCode {
  Return (Invoke-WebRequest $url | % {$_.StatusCode})

function Restart-Service {
  Stop-Service $serviceName
  Start-Service $serviceName
  Start-Sleep -s 10

$currentStatus = Get-URLStatusCode($healthURL)

Start-Transcript -Append $logFile

if ($currentStatus -ne $desiredStatusCode) {
  Write-Output "Returned $currentStatus"
  Get-Process -Name "Minio" | fl *
  Write-Output "Restarting service..."
  Restart-Service "MinIO"
  Write-Output "Restart command issued and waited 10s."

$currentStatus = Get-URLStatusCode($healthURL)
Write-Output "Returned $currentStatus"
Get-Process -Name "Minio" | ft


Now, create a Scheduled Task to run this script every 15 minutes

Drop the following commands into a PowerShell session to set up the Scheduled Task.

$action = New-ScheduledTaskAction -Execute 'Powershell.exe' `
  -Argument '-NoProfile -WindowStyle Hidden -File "$($PSScriptRoot)\checkMinio.ps1"'
$trigger = New-ScheduledTaskTrigger -Daily -At 12am
$task = Register-ScheduledTask -TaskName "CheckMinio-00" -Description "Check Minio localhost:9000 and restart" -Trigger $trigger -Action $action
$task.Triggers.Repetition.Duration = "P1D"
$task.Triggers.Repetition.Interval = "PT15M"
$task | Set-ScheduledTask

Publish and add end-to-end monitoring

I then usually deploy an nginx proxy in front, with a letsencrypt certificate at, and then add end-to-end monitoring hitting and alert if it doesn’t return status code 200. Be sure to change the firewall configuration as appropriate.

Set up rclone backups

I had a Debian VM that I wanted to sync/snapshot some data from. I configured rclone as follows:

$ cat .config/rclone/rclone.conf
type = s3
provider = Minio
env_auth = false
access_key_id = minio
secret_access_key = 123minio123
endpoint =
acl = private

Once rclone was configured, I ran a backup using:

$ rclone sync -P path/to/mydata fqdn-example-00:/path/to/mybackup

To schedule this as a cronjob, remove the -P (show progress) switch from the above command.