Guide

Enshrouded server runbook

Rebuild an Enshrouded dedicated server on Windows 11 from a bare install: SteamCMD, ports, an update script and a Windows service with a nightly restart.

Applies to Enshrouded dedicated server · Windows 11 Pro
Last verified 20 Jul 2026
Read 5 min

Following this document from a bare Windows install reproduces the exact working configuration, including everything ThangorodrimControl.exe depends on.


Fixed paths and names (hard dependencies)

ThangorodrimControl.exe has these values compiled in as constants. The stack must be rebuilt with these exact paths and names, or the exe must be recompiled with new constants (see section 9).

Item Value
SteamCMD D:\steamcmd\steamcmd.exe
Server install root D:\enshrouded-server\
Server executable D:\enshrouded-server\enshrouded_server.exe
Server config D:\enshrouded-server\enshrouded_server.json
World saves D:\enshrouded-server\savegame\
Update-time backup mirror D:\enshrouded-server\savegame-backup\
Update script D:\enshrouded-server\update-enshrouded.bat
Windows service name EnshroudedServer (NSSM)
Server display name Thangorodrim
Game/query port UDP 15637
LAN address 192.168.7.7 (barad-dur.arda)
External address hostname:15637 (static public IP)

No Steam account login or game purchase is required for the server — the dedicated server is free app 2278520, installed via SteamCMD anonymous login. It can run alongside the game client on the same machine and same account.

Install SteamCMD

  1. Download steamcmd.zip from Valve: https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip
  2. Extract to D:\steamcmd\ (not Program Files — it self-updates in place and wants write access to its own folder).
  3. Run steamcmd.exe once; it self-updates and drops to a Steam> prompt. Type quit.

Install the server

Run:

D:\steamcmd\steamcmd.exe +force_install_dir D:\enshrouded-server +login anonymous +app_update 2278520 validate +quit

+force_install_dir must come before +login or it is silently ignored. Download is ~8 GB. A fresh SteamCMD install sometimes fails its first attempt with Failed to install app '2278520' (Missing configuration) — simply run the same command again; if it persists, delete D:\steamcmd\appcache and retry.

This same command is the update mechanism after every game patch (client and server versions must match or joins fail).

Server configuration

enshrouded_server.json does not exist until first launch. Run enshrouded_server.exe once, let it start, close it, then edit the generated file. Settings in use:

  1. "name": "Thangorodrim"
  2. "ip": "0.0.0.0" — bind-all; leave as-is. Never put the public IP here (it is a bind address, not an advertisement address).
  3. "queryPort": 15637
  4. "slotCount": 16 (16 is the game’s hard cap, same as in-game hosting).
  5. "gameSettingsPreset": "Default" — presets are Relaxed / Default / Hard / Survival. Setting "Custom" activates the individual gameSettings block instead. Difficulty lives in this config, not in the world save.
  6. userGroups — the password IS the group selector at join time; no usernames exist. Every group must have a unique password. An empty password makes that group joinable by anyone (never leave Guest blank — it has canEditWorld). Groups in use: Admin (full rights, password private), Friend, Guest, Visitor, plus optionally a Builder group (canExtendBase: true, canKickBan: false). reservedSlots: 1 on Admin guarantees a join slot when full.

Restart the server after any config edit — the file is read only at startup.

Ports and port forwarding

  1. Windows Firewall: inbound allow rule for UDP 15637 (or for enshrouded_server.exe).
  2. OpenWrt port forward: UDP, source zone WAN, external port 15637 → destination zone LAN, 192.168.7.7, internal port 15637. NAT loopback/reflection is on by default in OpenWrt and handles internal use of the external name.
  3. DDNS hostname.provider.tld points at the static public IP - NOT CGNAT.
  4. Players join via Steam → View → Game Servers → Favorites → add hostname.provider.tld:15637 (LAN players can use 192.168.7.7:15637), or find “Thangorodrim” in the in-game browser, then enter their group’s password. Steam pins a favourite to the IP resolved at add-time — if DNS was wrong when added, remove and re-add.
  5. Bandwidth: ~2 Mbit/s upload per connected player; the 200/200 fibre line with Cake SQM (A+ bufferbloat, ~190 Mbps shaped) is over-provisioned for a full 16 slots.

Update script

D:\enshrouded-server\update-enshrouded.bat, exactly:

@echo off
REM Update script for NSSM workflow — the GUI (or you) stops/starts the
REM service; this script only backs up the save and runs SteamCMD.
REM "< CON" attaches SteamCMD's stdin to the (hidden) console so it does
REM not abort with "Input redirection is not supported" when launched
REM from ThangorodrimControl. Harmless when run by double-click too.
robocopy D:\enshrouded-server\savegame D:\enshrouded-server\savegame-backup /MIR >nul
D:\steamcmd\steamcmd.exe +force_install_dir D:\enshrouded-server +login anonymous +app_update 2278520 validate +quit < CON

Two properties matter and must be preserved:

  1. The script does NOT stop or start the server. Process lifecycle belongs to the NSSM service; ThangorodrimControl’s Update button performs graceful stop → this script → start.
  2. The < CON on the SteamCMD line is required. ThangorodrimControl launches the script in a hidden console; without < CON, SteamCMD aborts with “Input redirection is not supported”.

The robocopy /MIR keeps a single rolling pre-patch mirror in savegame-backup — it is overwritten on every update run. To restore: stop the service, copy savegame-backup contents over savegame, start. Restore BEFORE running another update. Independently of this, the game itself keeps 10 numbered rolling backup copies of the world inside savegame at all times.

NSSM service

NSSM 2.24 (64-bit exe from nssm.cc, extracted from the zip’s win64 folder, on PATH or invoked by full path). All commands in an ELEVATED prompt:

nssm install EnshroudedServer D:\enshrouded-server\enshrouded_server.exe
nssm set EnshroudedServer AppDirectory D:\enshrouded-server
nssm set EnshroudedServer AppExit Default Restart
nssm set EnshroudedServer AppExit 0 Exit
nssm set EnshroudedServer AppStopMethodConsole 30000
nssm start EnshroudedServer

What each choice does:

  1. AppExit Default Restart + AppExit 0 Exit — restart the server automatically if it crashes (non-zero exit), but stay stopped after a clean shutdown (exit 0). This prevents NSSM racing the update script or the GUI’s Stop button.
  2. AppStopMethodConsole 30000 — on service stop, NSSM sends a console Ctrl+C first and allows up to 30 s for the server to flush a final save and exit before escalating (WM_CLOSE → WM_QUIT → terminate). This is what makes every net stop graceful. NSSM 2.24’s default of 1500 ms is too short for a save flush.
  3. The service is created AUTO_START, so the server comes up on every boot, before login — this is the boot-start mechanism (Task Scheduler is NOT used for startup). Verify: sc qc EnshroudedServer shows START_TYPE : 2 AUTO_START.

Graceful-shutdown verification: the Steam connection log in D:\enshrouded-server\logs ends a clean stop with a LogOff() / AsyncDisconnect / Log session ended sequence. A hard kill shows the log simply cutting off.

On NSSM 2.24, nssm dump does not exist; use nssm edit EnshroudedServer (GUI) or nssm get EnshroudedServer <param>.

Nightly 04:00 restart

One scheduled task, created from an elevated prompt:

schtasks /create /tn "Enshrouded Nightly Restart" /tr "cmd /c net stop EnshroudedServer & timeout /t 5 /nobreak & net start EnshroudedServer" /sc daily /st 04:00 /ru SYSTEM /rl HIGHEST

Runs as SYSTEM (inherent service-control rights, runs logged-in or not). net stop goes through NSSM’s Ctrl+C sequence, so the nightly bounce is as graceful as a manual one. Test on demand: schtasks /run /tn "Enshrouded Nightly Restart". Any older tasks from the pre-NSSM setup (“Enshrouded Server Auto Startup”, old restart task) must be disabled or deleted — NSSM owns startup now.

ThangorodrimControl.exe

Native Win32 GUI, written in Go (pure standard library, no runtime dependencies), source in main.go alongside the exe. Embedded requireAdministrator manifest — double-clicking triggers UAC, which service control requires.

Buttons:

  1. Start — net start EnshroudedServer
  2. Stop (graceful) — net stop EnshroudedServer, waits and confirms STOPPED state
  3. Restart (graceful) — stop, 3 s pause, start
  4. Update — graceful stop → runs the update .bat in a hidden console → start, checking SteamCMD output for success

Status line polls the Service Control Manager API every 30 seconds (plus at launch and after every button operation) and logs externally caused state changes (4 AM bounce, crash restarts). The exe is a control panel only: the service runs with or without it, and it schedules nothing.

Compiled-in constants (top of main.go): serviceName = "EnshroudedServer", updateBat = "D:\enshrouded-server\update-enshrouded.bat". If any path or the service name changes, edit the constants and rebuild:

GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -H windowsgui" -o ThangorodrimControl.exe .

with the rsrc-generated .syso (admin manifest) present in the package directory. Three fixes are baked into the final version and must not be regressed: runtime.LockOSThread() pinning the UI thread (without it the window eventually hangs); the update script launched with CREATE_NEW_CONSOLE + hidden window (SteamCMD stdin fix, paired with the .bat’s < CON); service status via direct SCM API calls rather than parsing sc query output.

Reboot / power-loss behaviour

Cold boot or hardware reboot: SCM starts the NSSM service automatically before login; the server registers with Steam (visible in favourites within a minute or two of boot). Combined with the existing Wake-on-LAN setup, the machine can be woken remotely and the server self-starts with no interaction. If the server process crashes, NSSM restarts it (AppExit Default Restart). A clean stop (GUI, net stop, nightly task) stays stopped until deliberately started.

World save notes (migration reference)

World saves are slot-named static hexadecimals: world 1 = 3ad85aea, world 2 = 3bd85c7d, world 3 = 38d857c4, etc. The dedicated server loads slot 1 (3ad85aea). To import a client-hosted world: stop the server; locate the world’s complete file set (base, _info, -index, _info-index, matching timestamps) in %USERPROFILE%\Saved Games\Enshrouded or ...\Steam\userdata\<SteamID>\1203620\remote; back up and clear the server’s savegame; rename the set to slot-1 names; copy in; start. Player characters are stored client-side and are unaffected. Server name, groups, and difficulty live in the JSON config, not the save, and survive migration.