Skip to content

Instantly share code, notes, and snippets.

@Fronix
Last active March 9, 2026 15:01
Show Gist options
  • Select an option

  • Save Fronix/da7b33f92831087c1f4ef3ec4f111754 to your computer and use it in GitHub Desktop.

Select an option

Save Fronix/da7b33f92831087c1f4ef3ec4f111754 to your computer and use it in GitHub Desktop.
Meilisearch installer/updater for windows
# Provided as is
# Make sure to check the code!
# Folder for Meilisearch should include config.toml, this is not created automatically atm
# Script will require you to have the meilisearch binary exe on the filesystem
# IT WILL NOT FETCH FROM GITHUB!
# Now uses dumpless upgrade (--experimental-dumpless-upgrade) instead of dump/import
function Show-Menu {
Write-Host "================================="
Write-Host " Meilisearch Management Program"
Write-Host "================================="
Write-Host "1: Install Meilisearch"
Write-Host "2: Update Meilisearch"
Write-Host "3: Exit"
}
# Define the source path for WinSW.exe
$sourceWinSWPath = "D:\winsw.exe"
# Define the default base URI for Meilisearch
$defaultBaseUri = "http://localhost:7700/"
# Define the base URI for the temporary upgrade instance
$baseUriUpgrade = "http://localhost:7701/"
function Check-WinSW {
param (
[string]$meilisearchDir
)
# Define the destination path for WinSW.exe in the Meilisearch directory
$winswPath = "$meilisearchDir\winsw.exe"
# Check if WinSW.exe exists in the destination directory
if (-Not (Test-Path -Path $winswPath)) {
# Try to copy from C:\winsw
if (Test-Path -Path $sourceWinSWPath) {
Copy-Item -Path $sourceWinSWPath -Destination $winswPath
Write-Host "Copied WinSW.exe to $meilisearchDir"
} else {
Write-Host "WinSW.exe not found in $meilisearchDir or C:\winsw. Please ensure it is available before proceeding."
Write-Host "Press any key to return to the main menu..."
[void][System.Console]::ReadKey($true)
return $false
}
}
return $true
}
function WaitingForUser {
param (
[switch]$ExitProgram
)
Write-Host "Press any key to continue..."
[void][System.Console]::ReadKey($true)
if ($ExitProgram.IsPresent) {
Write-Host "Exiting the program..."
exit
} else {
return $false
}
}
function Check-MeiliHealth {
param (
[string]$meilisearchUrl
)
$headers = @{
"Content-Type" = "application/json"
}
if (-not $meilisearchUrl.EndsWith("/")) {
$meilisearchUrl = $meilisearchUrl + "/"
}
$healthUrl = $meilisearchUrl + "health"
try {
$healthResponse = Invoke-WebRequest -Uri $healthUrl -Method Get -Headers $headers
if ($healthResponse.StatusCode -eq 200) {
Write-Host "Meilisearch is healthy and running!"
return $true
} else {
Write-Host "Meilisearch is not running or healthy. Status code: $($healthResponse.StatusCode)"
return $false
}
}
catch {
return $false
}
}
function Get-CurrentVersionDir {
param (
[string]$meilisearchDir
)
$xmlPath = "$meilisearchDir\meilisearch.xml"
if (-Not (Test-Path -Path $xmlPath)) {
Write-Host "Service config not found at $xmlPath"
return $null
}
[xml]$config = Get-Content -Path $xmlPath
$workingDir = $config.service.workingdirectory
if ([string]::IsNullOrEmpty($workingDir)) {
Write-Host "Could not determine current version directory from service config."
return $null
}
return $workingDir
}
function Install-Meilisearch {
# Step 1: Ask for the full path of the Meilisearch executable
$meilisearchPath = Read-Host "Enter the full path of the Meilisearch executable (including the executable name)"
# Step 2: Ask for the Meilisearch version
$meilisearchVersion = Read-Host "Enter the Meilisearch version (e.g., v1.10.0)"
# Step 3: Ask for the drive where Meilisearch should be installed
$installDrive = Read-Host "Enter the drive letter where Meilisearch should be installed (e.g., E:)"
# Step 4: Create the Meilisearch folder on the specified drive if it doesn't exist
$meilisearchDir = "$installDrive\Meilisearch"
if (-Not (Test-Path -Path $meilisearchDir)) {
New-Item -Path $meilisearchDir -ItemType Directory
Write-Host "Created directory: $meilisearchDir"
}
# Check if WinSW is installed before proceeding
if (-Not (Check-WinSW -meilisearchDir $meilisearchDir)) {
return
}
# Step 5: Create a version-specific folder within the Meilisearch directory
$versionDir = "$meilisearchDir\$meilisearchVersion"
if (-Not (Test-Path -Path $versionDir)) {
New-Item -Path $versionDir -ItemType Directory
Write-Host "Created directory: $versionDir"
}
# Step 6: Copy the executable to the version directory, rename it, and delete the original
$destinationPath = "$versionDir\meilisearch.exe"
Copy-Item -Path $meilisearchPath -Destination $destinationPath
Write-Host "Copied Meilisearch executable to $destinationPath"
Write-Host "Don't forget to delete the copied file $meilisearchPath"
# Step 7: Setup or update the Meilisearch service using WinSW
Setup-MeilisearchService $meilisearchDir $destinationPath $meilisearchVersion
WaitingForUser
}
function Update-Meilisearch {
# Step 1: Ask for the new Meilisearch version
$newVersion = Read-Host "Enter the new Meilisearch version (e.g., v1.38.0)"
# Step 2: Ask for the drive where Meilisearch is installed
$installDrive = Read-Host "Enter the drive letter where Meilisearch is installed (e.g., E:)"
# Step 3: Ask for the full path of the new Meilisearch executable
$newMeilisearchPath = Read-Host "Enter the full path of the new Meilisearch executable (including the executable name)"
# Step 4: Ask for the URL of the Meilisearch instance to verify it's running
$meilisearchUrl = Read-Host "Enter the URL of the instance (leave empty for http://localhost:7700)"
if ([string]::IsNullOrEmpty($meilisearchUrl)) {
$meilisearchUrl = $defaultBaseUri
}
$meiliHealth = Check-MeiliHealth -meilisearchUrl $meilisearchUrl
if (-Not $meiliHealth) {
Write-Host "The old meilisearch instance is not running, start the service and try again."
WaitingForUser -ExitProgram
return
}
$meilisearchDir = "$installDrive\Meilisearch"
# Check if WinSW is installed before proceeding
if (-Not (Check-WinSW -meilisearchDir $meilisearchDir)) {
return
}
# Step 5: Determine the old version's directory from the service config
$oldVersionDir = Get-CurrentVersionDir -meilisearchDir $meilisearchDir
if ($null -eq $oldVersionDir) {
Write-Host "Could not determine the current version directory. Aborting."
WaitingForUser -ExitProgram
return
}
$oldDbPath = "$oldVersionDir\meili_db"
if (-Not (Test-Path -Path $oldDbPath)) {
Write-Host "Database not found at $oldDbPath. Aborting."
WaitingForUser -ExitProgram
return
}
Write-Host "Found existing database at: $oldDbPath"
# Step 6: Create the new version directory and copy the executable
$newVersionDir = "$meilisearchDir\$newVersion"
if (-Not (Test-Path -Path $newVersionDir)) {
New-Item -Path $newVersionDir -ItemType Directory
Write-Host "Created directory: $newVersionDir"
}
$destinationPath = "$newVersionDir\meilisearch.exe"
try {
Copy-Item -Path $newMeilisearchPath -Destination $destinationPath
Write-Host "Copied new Meilisearch executable to $destinationPath"
}
catch {
Write-Host "Copy error: $_"
WaitingForUser -ExitProgram
return
}
# Step 7: Stop the old service
$winswDir = $meilisearchDir
Write-Host "Stopping the current Meilisearch service..."
& "$winswDir\winsw.exe" stop "$winswDir\meilisearch.xml"
Start-Sleep -Seconds 3
Write-Host "Service stopped."
# Step 8: Copy the database to the new version directory
$newDbPath = "$newVersionDir\meili_db"
Write-Host "Copying database from $oldDbPath to $newDbPath (this may take a while for large databases)..."
try {
Copy-Item -Path $oldDbPath -Destination $newDbPath -Recurse
Write-Host "Database copied successfully."
}
catch {
Write-Host "Error copying database: $_"
Write-Host "Restarting old service..."
& "$winswDir\winsw.exe" start "$winswDir\meilisearch.xml"
WaitingForUser -ExitProgram
return
}
# Step 9: Run the new binary with --experimental-dumpless-upgrade to migrate the database
Write-Host "Starting dumpless upgrade with new binary..."
try {
$meilisearchProcess = Start-Process -FilePath "meilisearch.exe" `
-WorkingDirectory $newVersionDir `
-ArgumentList "--experimental-dumpless-upgrade", "--db-path", "./meili_db", "--http-addr", "localhost:7701", "--config-file-path", "../config.toml" `
-PassThru
Write-Host "Dumpless upgrade started (Process ID: $($meilisearchProcess.Id))"
Write-Host "Waiting for the upgrade to complete..."
Start-Sleep -Seconds 10
# Step 10: Wait for the upgrade instance to become healthy
$maxAttempts = 60
$attempt = 0
$upgradeHealthy = $false
while ($attempt -lt $maxAttempts) {
try {
$healthResponse = Invoke-WebRequest -Uri ($baseUriUpgrade + "health") -Method Get -Headers @{"Content-Type" = "application/json"}
if ($healthResponse.StatusCode -eq 200) {
Write-Host "Dumpless upgrade completed successfully!"
$upgradeHealthy = $true
break
}
}
catch {
Write-Host "Still upgrading..."
}
$attempt++
Start-Sleep -Seconds 5
}
# Step 11: Stop the temporary upgrade process
if (-Not $meilisearchProcess.HasExited) {
Stop-Process -Id $meilisearchProcess.Id -Force
Write-Host "Stopped temporary upgrade instance."
}
if (-Not $upgradeHealthy) {
Write-Host "Dumpless upgrade did not complete within the expected time. Check logs in $newVersionDir."
Write-Host "Restarting old service..."
& "$winswDir\winsw.exe" start "$winswDir\meilisearch.xml"
WaitingForUser -ExitProgram
return
}
}
catch {
Write-Host "Error during dumpless upgrade: $_"
if (-Not $meilisearchProcess.HasExited) {
Stop-Process -Id $meilisearchProcess.Id -Force
}
Write-Host "Restarting old service..."
& "$winswDir\winsw.exe" start "$winswDir\meilisearch.xml"
WaitingForUser -ExitProgram
return
}
Write-Host "Don't forget to delete the copied file $newMeilisearchPath"
Read-Host "Upgrade is done, ready to update the service. Press any key to continue..."
# Step 12: Update the Meilisearch service to point to the new version
Setup-MeilisearchService $meilisearchDir $destinationPath $newVersion
# Old version directory kept for rollback purposes (database copy preserved)
Write-Host "Old version directory kept at $oldVersionDir for rollback purposes."
WaitingForUser
}
# --- Dump functions kept as fallback utilities ---
function New-MeiliearchDump {
param (
[string]$apiKey,
[string]$baseUri
)
$headers = @{
"Authorization" = "Bearer $apiKey"
"Content-Type" = "application/json"
}
if (-not $baseUri.EndsWith("/")) {
$baseUri = $baseUri + "/"
}
$dumpsUri = $baseUri + "dumps"
$meiliHealth = Check-MeiliHealth -meilisearchUrl $baseUri
if (-Not $meiliHealth) {
Write-Host "The old meilisearch instance is not running, start the service and try again."
WaitingForUser -ExitProgram
return
}
try {
$dumpResponse = Invoke-RestMethod -Uri $dumpsUri -Method Post -Headers $headers
$taskId = $dumpResponse.taskUid
Write-Host "Dump creation initiated: $($dumpResponse.dumpUid)"
while ($true) {
$tasksUri = $baseUri + "tasks/$taskId"
$statusResponse = Invoke-RestMethod -Uri $tasksUri -Method Get -Headers $headers
if ($statusResponse.status -eq "succeeded") {
Write-Host "Dump is ready!"
break
} elseif ($statusResponse.status -eq "failed" -or $statusResponse.status -eq "canceled") {
Write-Host "Dump creation failed!"
return
}
Write-Host "Dump is not ready yet, waiting..."
Start-Sleep -Seconds 5
}
}
catch {
Write-Host "Error creating dump: $_"
WaitingForUser -ExitProgram
}
}
function Import-MeilisearchDump {
param (
[string]$dumpDir,
[string]$newMeilisearchPath
)
$headers = @{
"Content-Type" = "application/json"
}
$latestDump = Get-ChildItem -Path $dumpDir | Sort-Object LastWriteTime -Descending | Select-Object -First 1
try {
$baseUri = $baseUriUpgrade
$healthUrl = $baseUri + "health"
Write-Host "Starting dump import using the latest dump ($($latestDump.FullName))"
$meilisearchProcess = Start-Process -FilePath "meilisearch.exe" -WorkingDirectory $newMeilisearchPath -ArgumentList "--db-path", "./meili_db", "--import-dump", $latestDump.FullName, "--http-addr", "localhost:7701" -PassThru
Write-Host "Dump import started.... (Process ID: $($meilisearchProcess.Id))"
Write-Host "Waiting 10 seconds for the instance to start..."
Start-Sleep -Seconds 10
Write-Host "Waiting for dump import to be processed..."
while ($true) {
try {
$healthResponse = Invoke-WebRequest -Uri $healthUrl -Method Get -Headers $headers
if ($healthResponse.StatusCode -eq 200) {
Write-Host "Dump import completed successfully! Stopping import instance..."
Stop-Process -Id $meilisearchProcess.Id -Force
break
}
}
catch {
Write-Host "Connection failed, Still processing..."
}
Start-Sleep -Seconds 5
Write-Host "Still processing..."
}
}
catch {
Write-Host "Error importing dump: $_"
WaitingForUser -ExitProgram
}
}
# --- End dump functions ---
function Setup-MeilisearchService {
param (
[string]$meilisearchDir,
[string]$executablePath,
[string]$meilisearchVersion
)
$serviceName = "MeiliSearch"
$serviceDescription = "MeiliSearch Service - Version $meilisearchVersion"
$meilisearchConfigPath = "$meilisearchDir\config.toml"
$versionDir = "$meilisearchDir\$meilisearchVersion"
$winswConfig = @"
<service>
<id>$serviceName</id>
<name>MeiliSearch</name>
<description>$serviceDescription</description>
<executable>$executablePath</executable>
<logpath>%BASE%\logs</logpath>
<arguments>"--config-file-path" ../config.toml "--db-path" ./meili_db</arguments>
<log mode="roll-by-time">
<pattern>yyyyMMdd</pattern>
</log>
<startmode>automatic</startmode>
<priority>high</priority>
<autoRefresh>false</autoRefresh>
<workingdirectory>$versionDir</workingdirectory>
</service>
"@
$winswConfigPath = "$meilisearchDir\meilisearch.xml"
$winswConfig | Out-File -FilePath $winswConfigPath -Encoding utf8
$winswDir = $meilisearchDir
Write-Host "Created or updated WinSW service configuration file at $winswConfigPath"
if (Get-Service -Name $serviceName -ErrorAction SilentlyContinue) {
& "$winswDir\winsw.exe" stop "$winswDir\meilisearch.xml"
Write-Host "Stopped existing Meilisearch service"
}
& "$winswDir\winsw.exe" uninstall "$winswDir\meilisearch.xml"
Write-Host "Uninstalled old Meilisearch service using WinSW"
& "$winswDir\winsw.exe" install "$winswDir\meilisearch.xml"
Write-Host "Installed new Meilisearch service using WinSW"
& "$winswDir\winsw.exe" start "$winswDir\meilisearch.xml"
Write-Host "Started Meilisearch service"
}
function Main {
while ($true) {
Show-Menu
$choice = Read-Host "Select an option"
switch ($choice) {
"1" { Install-Meilisearch }
"2" { Update-Meilisearch }
"3" { Write-Host "Exiting..."; exit }
default { Write-Host "Invalid option. Please try again." }
}
}
}
# Start the program
Main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment