Cómo configurar el reinicio automático ante fallos (watchdog) en el servidor de MU
Arma un watchdog confiable que detecte cuándo GameServer, ConnectServer o DataServer caen y reinicie los procesos automáticamente, sin que tengas que estar frente al VPS.
Un servidor de MU que cae a las 3 de la mañana y solo vuelve cuando despiertas pierde jugadores, y reputación. El proceso del GameServer se cierra por un crash de memoria, un pico de conexiones, un paquete malformado de bot, y nadie está en el VPS para reabrirlo. El watchdog resuelve esto: es un vig
Un servidor de MU que cae a las 3 de la mañana y solo vuelve cuando despiertas pierde jugadores, y reputación. El proceso del GameServer se cierra por un crash de memoria, un pico de conexiones, un paquete malformado de bot, y nadie está en el VPS para reabrirlo. El watchdog resuelve esto: es un vigía que monitorea tus procesos y puertos todo el tiempo y, en el instante en que detecta que algo cayó o se trabó, reinicia lo que hace falta reiniciar, en el orden correcto, y te avisa. Esta guía arma un watchdog robusto usando solo PowerShell y el Programador de Tareas de Windows; después muestra cómo reforzarlo con NSSM y alertas. Todas las rutas, nombres de proceso y puertos aquí son ejemplos que varían por versión del MuServer; adáptalos a los tuyos.
Requisitos previos
Antes de armar el watchdog, confirma que tienes:
- Un VPS o máquina Windows (Server 2016/2019/2022 o Windows 10/11) con el MuServer ya arrancando manualmente sin error;
- Acceso de administrador a Windows (el watchdog necesita reiniciar procesos y crear tareas programadas);
- Los nombres exactos de los ejecutables de tu MuServer y los puertos que usa cada uno;
- Los archivos
.bato los.exeque usas hoy para encender cada servicio; - PowerShell 5.1 o superior (ya viene en Windows) con permiso de ejecución de scripts.
Levanta primero el mapa de tus procesos. Abre el Administrador de Tareas con el servidor encendido y anota los nombres exactos. Un conjunto típico de MuServer es así (varía por versión):
| Servicio | Ejecutable (ejemplo) | Puerto por defecto (ejemplo) | Función |
|---|---|---|---|
| DataServer | DataServer.exe | 55960 / 55970 | Puente entre GameServer y SQL |
| ConnectServer | ConnectServer.exe | 44405 | Lista de servidores y ruteo inicial |
| JoinServer | JoinServer.exe | 55901 | Login/autenticación de cuentas |
| GameServer | GameServer.exe / Main.exe | 55901–55910 | El mundo del juego en sí |
GameServer.exe a Main.exe, MuServer.exe o algo personalizado. Si el watchdog busca el nombre equivocado, nunca detecta la caída, o peor, mata el proceso incorrecto.Paso 1 — Habilitar la ejecución de scripts PowerShell
Por defecto Windows bloquea los scripts. Abre PowerShell como Administrador y libera la ejecución para scripts locales firmados o locales:
# Permite scripts locales; los scripts descargados necesitan firma
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine -Force
# Confirmar
Get-ExecutionPolicy -List
Esto es suficiente para correr el watchdog que reside en el propio disco del servidor.
Paso 2 — Estandarizar los scripts de inicio
El watchdog va a llamar a un script por servicio. Estandarizar esto ahora evita dolor después. Crea una carpeta C:\MuServer\watchdog\ y, dentro de ella, un .bat de start para cada servicio que respete el directorio de trabajo correcto (muchos MuServer solo arrancan si el working directory es la carpeta del propio ejecutable):
@echo off
REM start_gameserver.bat (ejemplo — ajusta ruta y nombre)
cd /d "C:\MuServer\GameServer"
start "" "GameServer.exe"
Haz el equivalente para start_dataserver.bat, start_connectserver.bat y start_joinserver.bat. El orden de arranque importa mucho y será respetado por el watchdog:
- DataServer (necesita el SQL Server ya corriendo);
- JoinServer;
- ConnectServer;
- GameServer.
services.msc. El watchdog se encarga del MuServer, pero si la base no arranca con Windows, el DataServer se va a estar dando la cabeza contra la pared eternamente.Paso 3 — Escribir el script watchdog
Este es el corazón del sistema. El script de abajo hace tres cosas para cada servicio: verifica si el proceso existe, verifica si el puerto está escuchando y, si alguno de los dos falla, dispara el start correspondiente, siempre respetando límites para no entrar en bucle. Guárdalo como C:\MuServer\watchdog\watchdog.ps1:
# ===== watchdog.ps1 (ejemplo — varia por version) =====
$ErrorActionPreference = "Stop"
$logFile = "C:\MuServer\watchdog\watchdog.log"
# Cada servicio: nombre del proceso, puerto a verificar, script de start, orden
$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) {
# Retorna $true si algo esta escuchando en el puerto TCP local
$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) { "proceso ausente" } else { "puerto $($s.Porta) sin escucha" }
Escrever-Log "[CAIDA] $($s.Nome): $motivo -> reiniciando"
# Si el proceso existe pero el puerto murio (traba), matar antes
if ($proc -and -not $portaOk) {
Escrever-Log "[KILL] $($s.Nome) trabado, cerrando PID $($proc.Id)"
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3
}
Start-Process -FilePath $s.Start -WindowStyle Minimized
# Delay para que el servicio arranque antes de verificar el siguiente en orden
Start-Sleep -Seconds 8
}
}
El detalle crucial está en el bloque que mata el proceso trabado: un GameServer puede seguir abierto en el Administrador de Tareas pero dejar de aceptar conexiones (deadlock, thread trabado). En ese caso, verificar solo el proceso diría "está todo bien" mientras nadie logra loguearse. Al verificar el puerto y derribar el zombi antes de reiniciar, cubres ambos escenarios.
Paso 4 — Protección contra el bucle de reinicio
Un watchdog sin freno es peligroso: si el GameServer crashea en el arranque (config equivocada, base fuera de servicio), el watchdog reinicia mil veces por minuto, llena el log, martilla el SQL y puede corromper datos. Agrega un contador con ventana de tiempo. Crea C:\MuServer\watchdog\watchdog-guard.ps1 que envuelva la lógica con un límite:
# ===== Control de tasa de reinicio =====
$stateFile = "C:\MuServer\watchdog\restart-state.json"
$maxReinicios = 5 # maximo de reinicios...
$janelaMinutos = 10 # ...dentro de esta ventana
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 # supero el limite: NO reinicia, solo alerta
}
$eventos += $agora.ToString("o")
$state[$nome] = $eventos
$state | ConvertTo-Json -Depth 5 | Set-Content $stateFile
return $true
}
Paso 5 — Programar el watchdog para que corra cada minuto
El Programador de Tareas de Windows dispara el script en un intervalo corto. Crea la tarea por PowerShell (más confiable que la interfaz gráfica):
$acao = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\MuServer\watchdog\watchdog.ps1"
# Disparador: cada 1 minuto, indefinidamente
$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 "Vigia de los procesos del MuServer"
Correr como SYSTEM garantiza que el watchdog funcione incluso sin nadie logueado en el VPS. Confirma que la tarea está activa:
Get-ScheduledTask -TaskName "MU Watchdog" | Get-ScheduledTaskInfo
Paso 6 — Alertas: saber que cayó, no solo reiniciar
El reinicio automático es genial, pero las caídas repetidas son síntoma de un problema real. Agrega una alerta simple vía webhook de Discord (el más común entre dueños de MU). Colócalo en el punto en que el watchdog detecta la caída o supera el límite:
function Alertar-Discord($mensagem) {
$webhook = "https://discord.com/api/webhooks/TU_WEBHOOK_AQUI"
$corpo = @{ content = "**[MU Watchdog]** $mensagem" } | ConvertTo-Json
try {
Invoke-RestMethod -Uri $webhook -Method Post -Body $corpo -ContentType "application/json"
} catch {
Escrever-Log "[ALERTA] fallo al enviar Discord: $_"
}
}
Llama a Alertar-Discord "GameServer cayo y fue reiniciado a las $(Get-Date -Format HH:mm)" dentro del bloque de reinicio, y una alerta más fuerte cuando el límite de reinicios se supera (ahí es hora de que abras el VPS e investigues). No pongas el webhook real en un repositorio público.
Paso 7 — Alternativa robusta con NSSM
Si prefieres tratar cada servicio del MuServer como un servicio de Windows de verdad, NSSM (Non-Sucking Service Manager) lo hace, y ya trae recuperación automática integrada. Descarga el nssm.exe y registra cada ejecutable:
# Registrar el GameServer como servicio (ejemplo)
nssm install MU-GameServer "C:\MuServer\GameServer\GameServer.exe"
nssm set MU-GameServer AppDirectory "C:\MuServer\GameServer"
# Reiniciar automaticamente si sale, con throttle de 60s
nssm set MU-GameServer AppExit Default Restart
nssm set MU-GameServer AppThrottle 60000
NSSM cubre bien el escenario "el proceso se cerró", con reinicio y throttle nativos. No cubre la traba con proceso vivo, por eso el enfoque más completo es usar NSSM para el ciclo de vida y mantener el script PowerShell solo para la verificación de puerto. Las dos técnicas se complementan.
Paso 8 — Probar el watchdog a propósito
Nunca confíes en un watchdog que no viste funcionar. Fuerza una caída controlada:
- Con el servidor en el aire, abre el Administrador de Tareas;
- Finaliza manualmente el
GameServer.exe; - Cronometra: en hasta 1 minuto (el intervalo de la tarea) el watchdog debe reabrirlo;
- Revisa el
watchdog.log: debe haber la línea[CAIDA] GameServer: proceso ausente -> reiniciando; - Confirma que la alerta llegó a Discord;
- Repite matando el DataServer para validar el orden de arranque.
Errores comunes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| El watchdog nunca detecta la caída | Nombre del proceso equivocado en el script | Verifica el nombre exacto en el Administrador de Tareas y ajusta Processo= |
| Reinicia en bucle infinito | Sin control de tasa; config rota | Aplica el guard de maxReinicios y corrige la causa raíz |
| El GameServer arranca sin base | Orden de arranque equivocado / SQL fuera de servicio | Garantiza DataServer y SQL Server antes del GameServer |
| El puerto aparece como caído pero el servicio está ok | Firewall o puerto distinto del real | Confirma el puerto con Get-NetTCPConnection -State Listen |
| La tarea no corre sin login | Corriendo como usuario común | Recrea la tarea con principal SYSTEM y RunLevel Highest |
| El proceso trabado nunca es matado | Solo verifica el proceso, no el puerto | Activa la verificación de puerto + Stop-Process del zombi |
| El log crece sin parar | Sin rotación de log | Trunca/archiva el .log semanalmente por tarea programada |
Lista de verificación de lanzamiento
- Nombres exactos de todos los ejecutables del MuServer verificados en el Administrador de Tareas
- Scripts
.batde start creados concd /dpara el directorio correcto de cada servicio - SQL Server y SQL Server Agent en inicio Automático
watchdog.ps1verificando proceso Y puerto de cada servicio- Control de tasa de reinicio activo (límite por ventana de tiempo)
- Bloque que mata el proceso trabado (puerto muerto, proceso vivo) funcionando
- Tarea programada corriendo como SYSTEM cada 1 minuto
- Alerta de Discord/webhook disparando en caída y al superar el límite
- Prueba real: maté el GameServer y volvió en < 1 minuto
- Prueba real: maté el DataServer y se respetó el orden de arranque
- Log con rotación semanal configurada
- Webhook y contraseñas fuera de cualquier archivo público/repositorio
Con el watchdog en el aire, tu servidor deja de depender de que estés despierto y frente al VPS. Las caídas se vuelven eventos de segundos registrados en el log, no horas de servidor offline. El paso siguiente natural es sumar backup automático de la base a la misma rutina de resiliencia, así quedas cubierto tanto contra caídas de proceso como contra pérdida de datos.
Preguntas frecuentes
¿Qué es un watchdog en un servidor de MU?
Es un proceso vigía que va comprobando si los ejecutables del MuServer (ConnectServer, DataServer, GameServer) están vivos y respondiendo. Cuando uno de ellos se cierra o se traba, el watchdog reinicia automáticamente ese proceso, manteniendo el servidor en línea sin intervención manual.
¿Es mejor monitorear por proceso o por puerto?
Los dos juntos. Comprobar solo el proceso detecta cuándo el .exe se cierra, pero no atrapa trabas en las que el proceso sigue abierto sin responder. Comprobar el puerto TCP confirma que el servicio realmente acepta conexiones. La combinación evita falsos positivos y falsos negativos.
¿Necesito un programa de pago para tener un watchdog?
No. Se puede armar un watchdog sólido solo con PowerShell + el Programador de Tareas de Windows, que ya vienen en el VPS. Herramientas como NSSM o un MuServer con watchdog integrado ayudan, pero lo esencial es gratuito.
¿El watchdog puede empeorar las cosas?
Puede, si está mal configurado. Un watchdog que reinicia en bucle demasiado rápido enmascara la causa raíz y corrompe la base de datos. Coloca siempre un límite de reinicios por ventana de tiempo, delays entre intentos y una alerta para que investigues.
¿Cuál es el orden correcto de reinicio de los servicios?
Primero el DataServer (conexión con la base), después el ConnectServer, y por último el GameServer/JoinServer. Reiniciar fuera de orden hace que el GameServer arranque sin base y derribe todo de nuevo. El detalle exacto varía por versión.