Brazil's biggest MU Online portal — since 2003
Tutorial Advanced Infrastructure

How to set up automatic restart on failure (watchdog) on a MU server

Build a reliable watchdog that detects when GameServer, ConnectServer or DataServer crash and restarts the processes automatically, without you needing to be sitting in front of the VPS.

BR Bruno · Updated on Jun 30, 2026 · ⏱ 22 min read
Quick answer

A MU server that crashes at 3 in the morning and only comes back when you wake up loses players — and reputation. The GameServer process closes from a memory crash, a spike in connections, a bot's malformed packet, and nobody is at the VPS to reopen it. The watchdog solves this: it's a guardian that

A MU server that crashes at 3 in the morning and only comes back when you wake up loses players — and reputation. The GameServer process closes from a memory crash, a spike in connections, a bot's malformed packet, and nobody is at the VPS to reopen it. The watchdog solves this: it's a guardian that monitors your processes and ports all the time and, the instant it detects that something has crashed or hung, restarts what needs restarting, in the right order, and notifies you. This guide builds a robust watchdog using only PowerShell and the Windows Task Scheduler — then shows how to reinforce it with NSSM and alerts. All the paths, process names and ports here are examples that vary by version of the MuServer; adapt them to yours.

Nota: This tutorial assumes a server that is already set up and working. If you haven't reached that point yet, start with the guide on how to create a MU Online server and come back here to make it crash-proof.

Prerequisites

Before building the watchdog, confirm that you have:

  • A VPS or Windows machine (Server 2016/2019/2022 or Windows 10/11) with the MuServer already coming up manually without errors;
  • Administrator access to Windows (the watchdog needs to restart processes and create scheduled tasks);
  • The exact names of your MuServer executables and the ports each one uses;
  • The .bat files or the .exe files you use today to start each service;
  • PowerShell 5.1 or higher (already included in Windows) with script execution allowed.

First, build the map of your processes. Open Task Manager with the server running and note the exact names. A typical MuServer set looks like this (it varies by version):

ServiceExecutable (example)Default port (example)Role
DataServerDataServer.exe55960 / 55970Bridge between GameServer and SQL
ConnectServerConnectServer.exe44405Server list and initial routing
JoinServerJoinServer.exe55901Account login/authentication
GameServerGameServer.exe / Main.exe55901–55910The game world itself
Atenção: Don't rely on memory for the names. Some packs rename GameServer.exe to Main.exe, MuServer.exe or something custom. If the watchdog looks for the wrong name, it will never detect the crash — or worse, kill the wrong process.

Step 1 — Enable PowerShell script execution

By default Windows blocks scripts. Open PowerShell as Administrator and allow execution for local or locally signed scripts:

# Allow local scripts; downloaded scripts require a signature
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine -Force

# Confirm
Get-ExecutionPolicy -List

This is enough to run the watchdog that lives on the server's own disk.

Step 2 — Standardize the startup scripts

The watchdog will call one script per service. Standardizing this now avoids pain later. Create a C:\MuServer\watchdog\ folder and, inside it, a start .bat for each service that respects the correct working directory (many MuServers only come up if the working directory is the folder of the executable itself):

@echo off
REM start_gameserver.bat  (example — adjust path and name)
cd /d "C:\MuServer\GameServer"
start "" "GameServer.exe"

Do the equivalent for start_dataserver.bat, start_connectserver.bat and start_joinserver.bat. The startup order matters a lot and will be respected by the watchdog:

  1. DataServer (needs SQL Server already running);
  2. JoinServer;
  3. ConnectServer;
  4. GameServer.
Dica: Set SQL Server and SQL Server Agent to Automatic startup in services.msc. The watchdog takes care of the MuServer, but if the database doesn't come up with Windows, the DataServer will bang its head against the wall forever.

Step 3 — Write the watchdog script

This is the heart of the system. The script below does three things for each service: it checks whether the process exists, checks whether the port is listening and, if either one fails, fires the corresponding start — always respecting limits so it doesn't enter a loop. Save it as C:\MuServer\watchdog\watchdog.ps1:

# ===== watchdog.ps1 (example — varies by version) =====
$ErrorActionPreference = "Stop"
$logFile = "C:\MuServer\watchdog\watchdog.log"

# Each service: process name, port to check, start script, order
$servicos = @(
    @{ Nome="DataServer";    Processo="DataServer";    Porta=55970; Start="C:\MuServer\watchdog\start_dataserver.bat";    Ordem=1 },
    @{ Nome="JoinServer";    Processo="JoinServer";    Porta=55901; Start="C:\MuServer\watchdog\start_joinserver.bat";    Ordem=2 },
    @{ Nome="ConnectServer"; Processo="ConnectServer"; Porta=44405; Start="C:\MuServer\watchdog\start_connectserver.bat"; Ordem=3 },
    @{ Nome="GameServer";    Processo="GameServer";    Porta=55901; Start="C:\MuServer\watchdog\start_gameserver.bat";    Ordem=4 }
)

function Escrever-Log($msg) {
    $linha = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')  $msg"
    Add-Content -Path $logFile -Value $linha
}

function Porta-Escutando($porta) {
    # Returns $true if something is listening on the local TCP port
    $conn = Get-NetTCPConnection -State Listen -LocalPort $porta -ErrorAction SilentlyContinue
    return [bool]$conn
}

foreach ($s in ($servicos | Sort-Object { $_.Ordem })) {
    $proc = Get-Process -Name $s.Processo -ErrorAction SilentlyContinue
    $portaOk = Porta-Escutando $s.Porta

    if (-not $proc -or -not $portaOk) {
        $motivo = if (-not $proc) { "process missing" } else { "port $($s.Porta) not listening" }
        Escrever-Log "[CRASH] $($s.Nome): $motivo -> restarting"

        # If the process exists but the port died (hang), kill it first
        if ($proc -and -not $portaOk) {
            Escrever-Log "[KILL] $($s.Nome) hung, terminating PID $($proc.Id)"
            Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
            Start-Sleep -Seconds 3
        }

        Start-Process -FilePath $s.Start -WindowStyle Minimized
        # Delay so the service can come up before checking the next one in order
        Start-Sleep -Seconds 8
    }
}

The crucial detail is in the block that kills the hung process: a GameServer can stay open in Task Manager but stop accepting connections (deadlock, stuck thread). In that case, checking only the process would say "everything's fine" while nobody can log in. By checking the port and taking down the zombie before restarting, you cover both scenarios.

Step 4 — Protection against restart loops

A watchdog with no brakes is dangerous: if the GameServer crashes on startup (wrong config, database down), the watchdog restarts it a thousand times a minute, fills the log, hammers SQL and can corrupt data. Add a counter with a time window. Create C:\MuServer\watchdog\watchdog-guard.ps1 that wraps the logic with a limit:

# ===== Restart rate control =====
$stateFile = "C:\MuServer\watchdog\restart-state.json"
$maxReinicios = 5          # maximum number of restarts...
$janelaMinutos = 10        # ...within this window

function Pode-Reiniciar($nome) {
    $agora = Get-Date
    $state = @{}
    if (Test-Path $stateFile) {
        $state = Get-Content $stateFile -Raw | ConvertFrom-Json -AsHashtable
    }
    $eventos = @()
    if ($state.ContainsKey($nome)) {
        $eventos = @($state[$nome] | Where-Object {
            ([datetime]$_ ) -gt $agora.AddMinutes(-$janelaMinutos)
        })
    }
    if ($eventos.Count -ge $maxReinicios) {
        return $false   # limit exceeded: do NOT restart, only alert
    }
    $eventos += $agora.ToString("o")
    $state[$nome] = $eventos
    $state | ConvertTo-Json -Depth 5 | Set-Content $stateFile
    return $true
}
Atenção: When the limit is exceeded, the watchdog must stop restarting and send an alert instead of insisting. Infinite restarts don't fix a configuration problem — they only hide the cause and generate a giant log. Five restarts in ten minutes is a good starting point (it varies by server).

Step 5 — Schedule the watchdog to run every minute

The Windows Task Scheduler fires the script at a short interval. Create the task via PowerShell (more reliable than the graphical interface):

$acao = New-ScheduledTaskAction -Execute "powershell.exe" `
  -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\MuServer\watchdog\watchdog.ps1"

# Trigger: every 1 minute, indefinitely
$gatilho = New-ScheduledTaskTrigger -Once -At (Get-Date) `
  -RepetitionInterval (New-TimeSpan -Minutes 1)

$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest

Register-ScheduledTask -TaskName "MU Watchdog" -Action $acao `
  -Trigger $gatilho -Principal $principal -Description "Guardian for the MuServer processes"

Running as SYSTEM ensures the watchdog works even with nobody logged into the VPS. Confirm that the task is active:

Get-ScheduledTask -TaskName "MU Watchdog" | Get-ScheduledTaskInfo

Step 6 — Alerts: know it crashed, not just restart it

Automatic restarting is great, but repeated crashes are a symptom of a real problem. Add a simple alert via a Discord webhook (the most common among MU owners). Place it at the point where the watchdog detects a crash or exceeds the limit:

function Alertar-Discord($mensagem) {
    $webhook = "https://discord.com/api/webhooks/YOUR_WEBHOOK_HERE"
    $corpo = @{ content = "**[MU Watchdog]** $mensagem" } | ConvertTo-Json
    try {
        Invoke-RestMethod -Uri $webhook -Method Post -Body $corpo -ContentType "application/json"
    } catch {
        Escrever-Log "[ALERT] failed to send Discord message: $_"
    }
}

Call Alertar-Discord "GameServer crashed and was restarted at $(Get-Date -Format HH:mm)" inside the restart block, and a stronger alert when the restart limit is exceeded (that's when it's time to open the VPS and investigate). Don't put the real webhook in a public repository.

Step 7 — A robust alternative with NSSM

If you'd rather treat each MuServer service as a real Windows service, NSSM (Non-Sucking Service Manager) does that — and it already comes with built-in automatic recovery. Download nssm.exe and register each executable:

# Register the GameServer as a service (example)
nssm install MU-GameServer "C:\MuServer\GameServer\GameServer.exe"
nssm set MU-GameServer AppDirectory "C:\MuServer\GameServer"

# Restart automatically if it exits, with a 60s throttle
nssm set MU-GameServer AppExit Default Restart
nssm set MU-GameServer AppThrottle 60000

NSSM covers the "the process closed" scenario well, with native restart and throttle. It does not cover a hang where the process is still alive — which is why the more complete approach is to use NSSM for the lifecycle and keep the PowerShell script just for the port check. The two techniques complement each other.

Step 8 — Test the watchdog on purpose

Never trust a watchdog you haven't seen work. Force a controlled crash:

  1. With the server up, open Task Manager;
  2. Manually end GameServer.exe;
  3. Time it: within 1 minute (the task interval) the watchdog should reopen it;
  4. Check the watchdog.log — there should be the line [CRASH] GameServer: process missing -> restarting;
  5. Confirm that the alert arrived on Discord;
  6. Repeat by killing the DataServer to validate the startup order.
Dica: Do this test during off-peak hours and announce that it's maintenance. Testing on a packed production server, if something goes wrong, becomes a public headache.

Common errors and fixes

SymptomLikely causeFix
Watchdog never detects a crashWrong process name in the scriptCheck the exact name in Task Manager and adjust Processo=
Restarts in an infinite loopNo rate control; broken configApply the maxReinicios guard and fix the root cause
GameServer comes up without a databaseWrong startup order / SQL downEnsure DataServer and SQL Server are up before the GameServer
Port shows as down but the service is fineFirewall or a different port from the real oneConfirm the port with Get-NetTCPConnection -State Listen
Task doesn't run without a loginRunning as a regular userRecreate the task with the SYSTEM principal and RunLevel Highest
Hung process never gets killedOnly checks the process, not the portEnable the port check + Stop-Process for the zombie
Log grows without stoppingNo log rotationTruncate/archive the .log weekly via a scheduled task

Launch checklist

  • Exact names of all MuServer executables verified in Task Manager
  • Start .bat scripts created with cd /d to the correct directory for each service
  • SQL Server and SQL Server Agent set to Automatic startup
  • watchdog.ps1 checking both the process AND the port of each service
  • Restart rate control active (limit per time window)
  • Block that kills a hung process (dead port, live process) working
  • Scheduled task running as SYSTEM every 1 minute
  • Discord/webhook alert firing on a crash and when the limit is exceeded
  • Real test: I killed the GameServer and it came back in < 1 minute
  • Real test: I killed the DataServer and the startup order was respected
  • Log configured with weekly rotation
  • Webhook and passwords kept out of any public file/repository

With the watchdog live, your server no longer depends on you being awake and in front of the VPS. Crashes become events lasting seconds recorded in the log, not hours of a server offline. The natural next step is to add automatic database backup to the same resilience routine — that way you're covered against both process crashes and data loss.

Frequently asked questions

What is a watchdog on a MU server?

It's a guardian process that keeps checking whether the MuServer executables (ConnectServer, DataServer, GameServer) are alive and responding. When one of them closes or hangs, the watchdog automatically restarts that process, keeping the server online with no manual intervention.

Is it better to monitor by process or by port?

Both together. Checking only the process detects when the .exe closes, but doesn't catch hangs where the process stays open without responding. Checking the TCP port confirms that the service actually accepts connections. Combining them avoids false positives and false negatives.

Do I need paid software to have a watchdog?

No. You can build a solid watchdog with just PowerShell + the Windows Task Scheduler, which already ship with the VPS. Tools like NSSM or a MuServer with a built-in watchdog help, but the essentials are free.

Can a watchdog make things worse?

It can, if poorly configured. A watchdog that restarts in a fast loop masks the root cause and corrupts the database. Always set a limit on restarts per time window, delays between attempts, and an alert so you can investigate.

What's the correct order to restart the services?

First the DataServer (the database connection), then the ConnectServer, and lastly the GameServer/JoinServer. Restarting out of order makes the GameServer come up without a database and take everything down again. The exact detail varies by version.

BR
Events, maps & items editor

Bruno specializes in MU Online events, maps, bosses and item economy. He documents every detail based on real gameplay.

Keep reading

Related articles