How to automate MU Online server file deployment
Learn to build a reliable deployment pipeline for your MU Online server, with copy scripts, ordered stopping and starting of services, automatic backup, rollback and scheduling.
Automating a MU Online server's deployment is the difference between a calm five-minute maintenance and an entire night copying files by hand, hoping you didn't forget anything. A typical private server runs several interdependent processes — ConnectServer, GameServer, DataServer, the events service
Automating a MU Online server's deployment is the difference between a calm five-minute maintenance and an entire night copying files by hand, hoping you didn't forget anything. A typical private server runs several interdependent processes — ConnectServer, GameServer, DataServer, the events service and the website — and they all read the same configuration files and binaries. Swapping an .exe or a .dat with the process running simply doesn't work on Windows, because the system keeps the file locked. On top of that, a copy done by hand is prone to human error: a forgotten file, a swapped folder, a lost permission. This tutorial shows how to turn that whole ritual into a predictable pipeline: a script that backs up the current state, takes the services down in the right order, syncs the new files, brings everything back up and, if something goes wrong, lets you return to the previous state with a single command. We'll use PowerShell as the example because it is native to Windows, where most MU servers run, but the logic applies to any tool. Remember: paths, service names and folder structure vary by emulator/version, so treat the examples as a skeleton to adapt, not as absolute truth.
Prerequisites
Before writing the first line of automation, have a clear map of your server. If you are still building the environment from scratch, it is worth reviewing the guide on how to create a MU Online server first, because automation only makes sense on top of a base that already works manually.
You will need:
- Administrative access to the server machine (the deploy touches services and protected folders).
- PowerShell 5.1 or higher, already included in Windows Server and Windows 10/11.
- A known folder structure: where the binaries (ConnectServer, GameServer, DataServer), the configuration files and the game data live.
- A source folder (staging) with the already-validated files that will be published.
- Enough disk space to keep at least the last 3 to 5 full backups of the server folder.
- Permission to manage the services via
Stop-Service/Start-Serviceor, if the processes run as ordinary applications, viaStop-Process/Start-Process.
An important detail: not every emulator registers its components as Windows services. Many run as simple executables open in a session. That changes how you stop and start each piece, and the script has to reflect your reality. Varies by emulator/version.
Understanding the stop and start order
The order matters more than it seems. The components of a MU server have clear dependencies:
| Component | Depends on | Stop order | Start order |
|---|---|---|---|
| DataServer | SQL Server | 3rd to stop | 1st to start |
| GameServer | DataServer | 2nd to stop | 2nd to start |
| ConnectServer | Registered GameServer | 1st to stop | 3rd to start |
| Website / panel | SQL Server | optional | last |
The practical rule: take them down top to bottom (from what players touch first) and bring them up bottom to top (from the foundations). The ConnectServer is the entry point, so it is the first to fall to prevent new logins during the swap. The DataServer is the base that talks to SQL, so it is the first to come back. Reversing this produces GameServers that start without being able to register and get stuck in an error loop.
Step 1 — Define variables and the script structure
Start by isolating everything that changes between environments into variables at the top of the script. This avoids hard-coded paths scattered through the code.
# deploy.ps1 - example (adapt paths and names to your emulator/version)
$ServerPath = "D:\MuServer" # production destination
$StagingPath = "D:\Deploy\staging" # new, validated files
$BackupRoot = "D:\Deploy\backups"
$Timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$BackupPath = Join-Path $BackupRoot $Timestamp
$LogFile = "D:\Deploy\logs\deploy_$Timestamp.log"
# Processes/services in STOP ORDER
$ProcessesToStop = @("ConnectServer","GameServer","DataServer")
# On startup we'll use the reverse order
Always adopt a Timestamp when creating the backup. It is what makes rollback possible and traceable: each deploy leaves a dated snapshot of the previous state.
Step 2 — Log everything
A deploy with no log is a deploy you can't debug at three in the morning. Create a simple log function that writes to the screen and to a file at the same time.
function Write-Log {
param([string]$Msg, [string]$Level = "INFO")
$line = "{0} [{1}] {2}" -f (Get-Date -Format "HH:mm:ss"), $Level, $Msg
Write-Host $line
Add-Content -Path $LogFile -Value $line
}
Call Write-Log at every relevant step: start, backup completed, each service stopped, copy finished, each service started. When something fails, the log will tell you exactly where the pipeline stalled.
Step 3 — Backup before anything else
This is the non-negotiable step. Never overwrite a production file without first copying the current state to a safe place. The pre-deploy backup is your parachute.
Write-Log "Starting backup of $ServerPath"
New-Item -ItemType Directory -Path $BackupPath -Force | Out-Null
robocopy $ServerPath $BackupPath /E /R:2 /W:3 /NFL /NDL /NP | Out-Null
if ($LASTEXITCODE -ge 8) {
Write-Log "Backup FAILED. Aborting deploy." "ERROR"
exit 1
}
Write-Log "Backup completed at $BackupPath"
A note about robocopy: its exit codes don't follow the usual convention. Values from 0 to 7 indicate success (with or without files copied); 8 or higher indicate a real error. That is why the check is -ge 8 and not -ne 0. Ignoring this detail makes the script abort for no reason on every run.
Note that this backup covers only the server's files. The database has its own life cycle and should not be touched by file deployment — it needs a separate backup routine on SQL Server. Mixing the two is a classic source of headaches.
Step 4 — Stop the services in the correct order
With the backup in place, take the processes down. The handling depends on how your emulator runs each piece.
foreach ($proc in $ProcessesToStop) {
Write-Log "Stopping $proc"
$p = Get-Process -Name $proc -ErrorAction SilentlyContinue
if ($p) {
$p | Stop-Process -Force
Start-Sleep -Seconds 2
Write-Log "$proc stopped"
} else {
Write-Log "$proc was not running" "WARN"
}
}
If, in your case, the components are registered Windows services, swap it for Stop-Service -Name $proc -Force. The Start-Sleep gives the system time to release the file locks before we attempt the copy. Without that pause, the next step can fail with "file in use". It varies by emulator/version whether the components are services or loose processes.
Step 5 — Sync the new files
Now that nothing is holding the files, copy the staging into production. Use robocopy again, being careful not to delete data that should not be touched.
Write-Log "Copying files from $StagingPath to $ServerPath"
robocopy $StagingPath $ServerPath /E /R:2 /W:3 /XD "Backup" /NFL /NDL /NP | Out-Null
if ($LASTEXITCODE -ge 8) {
Write-Log "Copy FAILED. Starting rollback." "ERROR"
# rollback call here (Step 7)
exit 1
}
Write-Log "Files synced successfully"
Notice the /XD (exclude directory): use it for folders the deploy should never alter, like logs, dumps or persistent data. If you use the /MIR (mirror) flag, be very careful — it deletes at the destination everything that doesn't exist at the source, which can destroy local configurations. For an incremental deploy, prefer /E (copies subfolders, including empty ones) without mirroring.
Step 6 — Start the services in reverse order
Foundations first. Start in the order opposite to the stop.
$ProcessesToStart = @(
@{ Name="DataServer"; Exe="D:\MuServer\DataServer\DataServer.exe" }
@{ Name="GameServer"; Exe="D:\MuServer\GameServer\GameServer.exe" }
@{ Name="ConnectServer"; Exe="D:\MuServer\ConnectServer\ConnectServer.exe" }
)
foreach ($s in $ProcessesToStart) {
Write-Log "Starting $($s.Name)"
Start-Process -FilePath $s.Exe
Start-Sleep -Seconds 5
$ok = Get-Process -Name $s.Name -ErrorAction SilentlyContinue
if ($ok) { Write-Log "$($s.Name) online" }
else { Write-Log "$($s.Name) did NOT start" "ERROR" }
}
The 5-second Start-Sleep between each piece gives the DataServer time to register before the GameServer tries to connect, and for the GameServer to be ready before the ConnectServer accepts players. Adjust the time to your machine's speed. Varies by emulator/version.
Step 7 — Automatic rollback
Rollback is simply the inverse of the deploy: stop the services, restore the backup folder and start again. Wrap it in a reusable function.
function Invoke-Rollback {
param([string]$BackupDir)
Write-Log "ROLLBACK from $BackupDir" "WARN"
foreach ($proc in $ProcessesToStop) {
Get-Process -Name $proc -ErrorAction SilentlyContinue | Stop-Process -Force
}
Start-Sleep -Seconds 3
robocopy $BackupDir $ServerPath /E /R:2 /W:3 /NFL /NDL /NP | Out-Null
Write-Log "Files restored. Start the services manually and validate."
}
Call this function whenever a critical step fails, passing that deploy's $BackupPath. The key point is that the backup was created before any change, so the restore returns the server exactly to the state that worked. Keep the most recent backups and clean the old ones periodically so you don't fill the disk.
# Retention: keep only the 5 most recent backups
Get-ChildItem $BackupRoot -Directory |
Sort-Object Name -Descending |
Select-Object -Skip 5 |
Remove-Item -Recurse -Force
Step 8 — Post-deploy validation
Starting the processes doesn't guarantee the server is healthy. Do objective checks:
- Confirm that each process appears in the list (
Get-Process). - Test whether the ConnectServer and GameServer ports are listening with
Test-NetConnection -ComputerName 127.0.0.1 -Port <port>. - Do a test login with an account reserved for that.
- Check the components' own logs for registration or database-connection errors.
Only declare the maintenance over after a real login works. A process that is "up" but doesn't accept connections is worse than a process that is down, because it gives a false sense of success.
Step 9 — Scheduling with the Task Scheduler
For recurring or early-morning deploys, register the script in the Windows Task Scheduler. Do this only with scripts that have been tested exhaustively.
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File D:\Deploy\deploy.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 04:00
Register-ScheduledTask -TaskName "MU Deploy" -Action $action -Trigger $trigger `
-RunLevel Highest -Description "Automatic MU server deploy"
Even when scheduled, keep someone on call for the first runs and configure sending the logs by email or webhook. Automation doesn't eliminate the need for supervision — it only reduces the repetitive effort.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| "File in use" during the copy | Service was not fully stopped | Increase the Start-Sleep after stopping and confirm with Get-Process |
| Script always aborts at the backup | Check using -ne 0 on robocopy | Switch to -ge 8, since 1–7 are success |
| GameServer starts and crashes in a loop | DataServer wasn't ready yet | Increase the pause between DataServer and GameServer |
| Local config disappeared after deploy | Use of /MIR deleted what wasn't in staging | Use /E without mirroring and /XD for protected folders |
| Rollback doesn't restore the state | Backup created after the change | Make sure the backup runs before any copy |
| Scheduled task doesn't run as admin | Missing -RunLevel Highest | Recreate the task with elevated privilege |
| Ports don't respond after startup | Start order reversed | Start DataServer → GameServer → ConnectServer |
Launch checklist
- Folder structure and service names mapped and correct in the script
- Staging folder contains only files already validated in the test environment
- Log function writing to a dated file
- Pre-deploy backup tested and with the
-ge 8check - Backup retention policy configured (last 3–5)
- Stop order: ConnectServer → GameServer → DataServer
- Start order: DataServer → GameServer → ConnectServer
- Copy flags reviewed (no accidental
/MIR;/XDon protected folders) - Rollback function tested by restoring a real backup
- Post-deploy validation includes a test login with a reserved account
- Database backup handled by a separate routine
- Maintenance window announced to players
- Person on call assigned to watch the logs
With this pipeline in place, deployment stops being a source of anxiety and becomes a routine, reversible operation. The biggest gain is not the time saved, but the confidence: you know that, if something goes wrong, the dated backup and the rollback function put the server back online in minutes. Start simple, run it manually a few times, and only then schedule it. Adapt every path and service name to your emulator's reality, because these details always vary by emulator/version.
Frequently asked questions
Do I need to stop the GameServer to swap files?
Yes. Binaries and DLLs in use are locked by Windows, so the deploy has to take ConnectServer and GameServer down before overwriting any executable. Loose data files sometimes tolerate a hot swap, but never rely on that in production.
How often should I deploy?
Deploy whenever there is a change validated in the test environment. The ideal is a small, frequent deploy, with an announced maintenance window, rather than large accumulated packages that make rollback riskier.
Does the pre-deploy backup replace the database backup?
No. The pre-deploy backup protects the server's files (binaries and configs). The database needs its own backup routine via SQL Server, since file deployment should not touch the live database.
How do I roll back if the deploy breaks the server?
Restore the backup folder the script itself created before the copy, and start the services again. That is why the script should always version the backup with a date and time before overwriting any file.
Can I schedule the deploy for the early morning?
Yes, with the Windows Task Scheduler. It is recommended to schedule only already-tested deploys, announced to players in advance, keeping someone on call to watch the startup logs.