Loosely Decoupled
GuidePowerShellAutomation

Start a 7 Days to Die Dedicated Server at Boot on Windows and Stop It Cleanly

Run a 7 Days to Die server from a Windows scheduled task with no time limit, and stop it over telnet so the shutdown finishes and the world is saved.

A pigeon in a hard hat carefully lowers a snuffer over a lantern with a cyan flame, beside a neat stack of papers, while a big power switch on the wall is left alone.
On this page

TL;DR

Register a scheduled task whose action is the server executable itself, with an at-startup trigger, the SYSTEM account and the execution time limit set to zero. Stop the server with its telnet shutdown command, which the server accepts on 127.0.0.1:8081 by default.

It is the only stop I found that finishes the shutdown. The process exited with code 0 and wrote every save file. Stop-ScheduledTask ended it in about a second with exit code 1067 and wrote about half of them. The default time limit is 72 hours, after which Task Scheduler stops the server the same abrupt way.

In an elevated PowerShell on the server, with your config file (myserver.xml here) in the install folder and UserDataFolder set in it:

PowerShell
$dir = 'C:\7DaysToDieServer'
$log = Join-Path $dir 'output_log.txt'
$flags = '-quit -batchmode -nographics -configfile=myserver.xml -dedicated'
$arguments = "-logfile `"$log`" $flags"
$action = New-ScheduledTaskAction -Execute (Join-Path $dir '7DaysToDieServer.exe') `
    -Argument $arguments -WorkingDirectory $dir
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) `
    -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Register-ScheduledTask -TaskName '7DaysToDieServer' -Action $action -Trigger $trigger `
    -Settings $settings -User 'SYSTEM' -RunLevel Highest

Then Start-ScheduledTask -TaskName 7DaysToDieServer starts it, and the Stop-GameServer function in step 4 stops it. The game-specific parts are the server’s flags, its ready line, and telnet as the way to stop it.

I didn’t restart the test server, so the at-startup trigger is confirmed to be registered, not confirmed to fire. Step 5 has the check to run after your first reboot.

The previous part left a server you start by hand. This part makes Windows start it when the machine boots, keep it running, and stop it without losing the world.

1. Register the task

The task runs the server executable directly. Nothing wraps it, so the task’s state says whether the server is running, and stopping the task stops the server.

The previous part’s UserDataFolder setting matters here. A task that runs as SYSTEM has no user profile of its own, and without that setting the server writes its world under the SYSTEM profile, at C:\Windows\System32\config\systemprofile\AppData\Roaming\7DaysToDie. Check that your myserver.xml has it set before you register the task.

This registers the task:

PowerShell
$dir = 'C:\7DaysToDieServer'
$log = Join-Path $dir 'output_log.txt'
$flags = '-quit -batchmode -nographics -configfile=myserver.xml -dedicated'
$arguments = "-logfile `"$log`" $flags"
$action = New-ScheduledTaskAction -Execute (Join-Path $dir '7DaysToDieServer.exe') `
    -Argument $arguments -WorkingDirectory $dir
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) `
    -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Register-ScheduledTask -TaskName '7DaysToDieServer' -Action $action -Trigger $trigger `
    -Settings $settings -User 'SYSTEM' -RunLevel Highest

What each part does:

  • -WorkingDirectory sets the folder the server starts in. The server found myserver.xml next to its executable without it, so this is a precaution and not a requirement.
  • -AtStartup runs the task when Windows starts, before anyone logs on.
  • -User 'SYSTEM' with -RunLevel Highest runs it as the SYSTEM account, with no password to store. SYSTEM has machine-wide rights, so a flaw in the server or a mod it loads would have them too. If players you don’t trust can reach the server, register the task under a dedicated local account that can write only to the install folder. I didn’t test that.
  • -ExecutionTimeLimit ([TimeSpan]::Zero) turns the time limit off. The next step shows why that is the setting to get right.
  • -AllowStartIfOnBatteries and -DontStopIfGoingOnBatteries turn off two defaults that exist for laptops. Both are on by default, and a virtual machine has no battery, so these are harmless there.

Register-ScheduledTask prints an error if the name already exists, and leaves the first task as it was. Running the block twice is harmless. To change a task, remove it with Unregister-ScheduledTask -TaskName '7DaysToDieServer' -Confirm:$false and register it again.

Read the settings back:

Command
PS> $task = Get-ScheduledTask -TaskName '7DaysToDieServer'
PS> $task.Settings.ExecutionTimeLimit

Output

PT0S
Command
PS> $task.Triggers[0].CimClass.CimClassName

Output

MSFT_TaskBootTrigger
Command
PS> $task.Principal.UserId

Output

SYSTEM

PT0S is how Task Scheduler stores no limit.

2. Turn off the 72-hour execution time limit

A task registered with no settings read back PT72H. Task Scheduler ends a task that runs longer than its limit, and a game server is meant to run for weeks.

To see what it does to the server, I registered the same task with a limit of two minutes and started it. The server ran until the limit, and Task Scheduler then ended it, about 123 seconds after the start. The task went back to Ready with a last result of 267014, which is Task Scheduler’s code for a task that was terminated. The log shows the server beginning its shutdown, but the limit ends the process the same abrupt way Stop-ScheduledTask does in step 4, which is not a clean stop.

A longer limit only moves that moment. With PT72H the server would have been stopped three days after each start, and nothing would have restarted it. The -ExecutionTimeLimit ([TimeSpan]::Zero) in step 1 turns the limit off.

There is a second way to build this task, and I tested it too. The action starts a small script that launches the server detached, with Start-Process, and exits. The task then finishes in seconds, and the limit never applies. With a two-minute limit the server was still running after more than three minutes, the task itself had finished within seconds, and Stop-ScheduledTask left the server running. That design needs the telnet stop in step 4 anyway. I used the direct task because its state says whether the server is running.

3. Start the task and wait for the server

Start-ScheduledTask returns as soon as the task starts. The server takes about 40 seconds to be ready. This starts the task and waits for the server’s own ready line:

PowerShell
$log = 'C:\7DaysToDieServer\output_log.txt'
if (Get-Process 7DaysToDieServer -ErrorAction SilentlyContinue) {
    throw 'A 7DaysToDieServer process is already running. Stop it first.'
}
Remove-Item -LiteralPath $log -ErrorAction SilentlyContinue
Start-ScheduledTask -TaskName '7DaysToDieServer'
$deadline = (Get-Date).AddMinutes(5)
$ready = $false
while (-not $ready -and (Get-Date) -lt $deadline) {
    $ready = (Test-Path $log) -and
        (Select-String -Path $log -Pattern 'StartGame done' -Quiet)
    if (-not $ready) { Start-Sleep -Seconds 2 }
}
if (-not $ready) { throw 'The server was not ready after five minutes.' }
$task = Get-ScheduledTask -TaskName '7DaysToDieServer'
'Task state: {0}; last result: {1}' -f $task.State,
    (Get-ScheduledTaskInfo -TaskName '7DaysToDieServer').LastTaskResult

It stops if a 7DaysToDieServer process is already running, however it was started. A task in the Ready state would start a second server on the same ports and folder if you had started the first one by hand. And Start-ScheduledTask on a running task does nothing and returns no error. It does set the last result to 2147946720, the refusal from the default one-instance rule, and a later read of the result would mislead. It deletes the old log first. The check looks for StartGame done, and a leftover log would satisfy it at once.

While the server runs, the output is:

Text
Task state: Running; last result: 267009

A state of Running and a last result of 267009 mean the task is running. That is a status code and not an error.

4. Stop the server with the telnet shutdown command

The server has a telnet console, on 127.0.0.1:8081 by default, and its shutdown command is the stop that works. Setting TelnetPassword makes the console listen on every interface instead, so keep Windows Firewall and any cloud network security group closed to that port. This function sends the command, with the password first if there is one, and waits for the process to exit:

PowerShell
function Stop-GameServer {
    param([securestring]$Password, [int]$Port = 8081,
        [int]$TimeoutSeconds = 90)
    $server = @(Get-Process 7DaysToDieServer -ErrorAction SilentlyContinue)
    if ($server.Count -ne 1) {
        throw "Expected one 7DaysToDieServer process, found $($server.Count)."
    }
    $null = $server[0].Handle
    $client = New-Object Net.Sockets.TcpClient('127.0.0.1', $Port)
    try {
        $stream = $client.GetStream()
        $stream.ReadTimeout = 2000
        $buffer = New-Object byte[] 4096
        $reply = {
            $text = ''
            $until = (Get-Date).AddSeconds(3)
            try {
                while ((Get-Date) -lt $until -and
                    ($count = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) {
                    $text += [Text.Encoding]::ASCII.GetString($buffer, 0, $count)
                }
            }
            catch [IO.IOException] { }
            $text
        }
        $send = {
            param($line)
            $bytes = [Text.Encoding]::ASCII.GetBytes("$line`r`n")
            $stream.Write($bytes, 0, $bytes.Length)
        }
        if ($Password -and $Password.Length -gt 0) {
            & $send ([Net.NetworkCredential]::new('', $Password).Password)
            $login = & $reply
            if ($login -match 'Password incorrect') {
                throw 'The telnet password is missing or wrong.'
            }
            if ($login -notmatch 'Logon successful') {
                throw 'The server did not confirm the telnet login.'
            }
        }
        & $send 'shutdown'
        if ((& $reply) -match 'Password incorrect') {
            throw 'The telnet password is missing or wrong.'
        }
    }
    finally { $client.Close() }
    if (-not $server[0].WaitForExit($TimeoutSeconds * 1000)) {
        throw "The server was still running after $TimeoutSeconds seconds."
    }
    'Stopped, exit code {0}.' -f $server[0].ExitCode
}

It stops with an error and doesn’t touch the process in two cases. The telnet port refuses the connection, which is what TelnetEnabled set to false does. Or the server answers Password incorrect. It never forces the process to end. Run it as Stop-GameServer. With a telnet password, read it as a secure string so it stays out of your console history and any transcript:

PowerShell
$telnet = Read-Host 'Telnet password' -AsSecureString
Stop-GameServer -Password $telnet

The function turns it back into text only to send it. The game’s telnet console takes it unencrypted, one more reason to keep that port closed.

On the task’s server it stopped the process with exit code 0. The task went back to Ready with a last result of 0, because the process ended by itself. The server wrote 9 to 14 files under Saves and its log ran on to the end of its shutdown, after SaveAndCleanupWorld and Saving 0 of chunks, with 47 more lines. It took two to eight seconds.

With a telnet password set, I connected three ways and sent shutdown each time. With no password, or a wrong one, the server answered Password incorrect, please enter password: and kept running, and the function reports that at once. With the right password it stopped in two seconds, exit code 0, and the log held SaveAndCleanupWorld.

Each wrong attempt counts toward the server’s TelnetFailedLoginLimit, which was 10 on this server. The function sends the password first and reads the reply, for at most three seconds, and sends shutdown only after the server has said Logon successful, so a wrong password uses one attempt. Sent together, the shutdown after a wrong password would count as a second one. With the limit set to 3, a wrong password and then a missing one still left the right password working.

If the reply is late or says neither, the function stops with The server did not confirm the telnet login. The server also prints its log to a telnet connection, so a read that waited only for silence could go on as long as the server logged something every two seconds.

Why Stop-ScheduledTask isn’t a clean stop

Stop-ScheduledTask -TaskName '7DaysToDieServer' is the obvious stop for a task, and it ends the process, but not cleanly. I ran it twice on the task’s server:

  • The process exited with code 1067 after about a second. 1067 is ERROR_PROCESS_ABORTED, “The process terminated unexpectedly”, and it is the exit code the process had both times Task Scheduler ended it here.
  • The log began the shutdown, with SaveAndCleanupWorld and Stopping server, and then ended at Cleared queues. instead of running on for another 40 or so lines.
  • It wrote three to five files under Saves, among them main.ttw, players.xml and decoration.7dt. The telnet stop also wrote vehicles.dat, power.dat, turrets.dat, drones.dat and blockLimits.dat, and none of those five were written in either Stop-ScheduledTask run.
  • The task read Ready with a last result of 267014, Task Scheduler’s code for a terminated task.

I ran these on a new world with nobody connected, so I can’t say what the missing files hold in a world that people are playing. Their names suggest vehicles, power, turrets, drones and block limits. Use Stop-ScheduledTask as the fallback when telnet isn’t available.

Other ways to stop it: taskkill and the close request

With telnet off, I tried the other ways a script might stop the process. The first two behave differently when a console session started the server and when the task did, so the table has both:

MethodServer started by the taskServer started from a console session
CloseMainWindow() on the processReturned False, and the server kept runningReturned True, and the server was still running 10 seconds later
taskkill /PID <id> without /FERROR: ... This process can only be terminated forcefully (with /F option). The server kept runningSUCCESS: Sent termination signal, and the server was still running five seconds later
Stop-Process -ForceNot runEnded it at once. The log has no SaveAndCleanupWorld

The server runs with -batchmode -nographics, and the two polite requests had no effect on it. Stop-Process -Force is the last resort. After it, the server had logged no save, so whatever happened since the last save is gone.

5. Check the task after the first reboot

The at-startup trigger was registered and read back as a boot trigger, and the task starts on command. I didn’t restart the test server, so I haven’t seen the trigger fire. After your first reboot, check that it did:

PowerShell
Get-ScheduledTaskInfo -TaskName '7DaysToDieServer' |
    Select-Object LastRunTime, LastTaskResult
Get-Process 7DaysToDieServer -ErrorAction SilentlyContinue

A LastRunTime near the boot time and a running process mean it worked. Stop the server with step 4 before you restart the machine on purpose. I haven’t tested what Windows does to the server when it shuts down with the task running, and if it ends the process the way Stop-ScheduledTask does, the save is only partial.

Nothing here restarts the server if it crashes. The task has no restart settings.

Common failures

Server stops by itself after three days

The task has the default 72-hour limit, or whatever limit you set. Read it with (Get-ScheduledTask -TaskName '7DaysToDieServer').Settings.ExecutionTimeLimit. If it isn’t PT0S, remove the task and register it again with step 1.

Task starts and the server isn’t running

The log in -logfile has the reason. Two causes seen on this server: a missing Visual C++ runtime, and a ServerPort outside 1024 to 65530.

Stop-GameServer says the connection was refused

Error

Exception calling ".ctor" with "2" argument(s): "No connection could be made because the target machine actively refused it 127.0.0.1:8081"

Telnet is off, or on another port. Check TelnetEnabled and TelnetPort in myserver.xml. Until telnet works, Stop-ScheduledTask is the fallback, with the cost step 4 describes.

Last result 267014, or exit code 1067, after a stop

Task Scheduler uses it for a task that was ended from outside, which is what Stop-ScheduledTask and the time limit do. The process itself exits with 1067 when Task Scheduler ends it. A telnet stop leaves 0 for both. Step 4 shows what the difference cost.

Tested with

OS
Windows Server 2025 Datacenter (10.0.26100), virtual machine
PowerShell
Windows PowerShell 5.1, elevated
Game
7 Days to Die dedicated server (294420), V 3.2.0 (b10)
Date
October 2026

New articles, by email.

A short note when something new is published.